|
1 | 1 | # Approvals (v0) |
2 | 2 |
|
3 | | -Enterprises do not just want “the policy allowed it.” They want governance evidence: |
| 3 | +Enterprises don’t just want “policy allowed it.” They want governance evidence: |
| 4 | +- who approved, |
| 5 | +- what exactly was approved, |
| 6 | +- when it was approved, |
| 7 | +- and (optionally) a signature from the approver identity. |
4 | 8 |
|
5 | | -- who approved |
6 | | -- what exactly was approved |
7 | | -- when it was approved |
8 | | -- whether the approval can be independently verified |
| 9 | +IX-Agent-Notary models this as structured objects inside: `policy.approvals[]`. |
9 | 10 |
|
10 | | -IX-Agent-Notary models that evidence as structured objects inside `policy.approvals[]`. |
| 11 | +--- |
11 | 12 |
|
12 | | -## Approval object |
| 13 | +## Approval object (schema-backed) |
13 | 14 |
|
14 | 15 | Each approval is a JSON object with these required fields: |
15 | 16 |
|
16 | | -- `approval_id` — unique identifier for the approval record |
17 | | -- `type` — one of: |
18 | | - - `human` |
19 | | - - `ticket` |
20 | | - - `breakglass` |
21 | | -- `status` — one of: |
22 | | - - `requested` |
23 | | - - `approved` |
24 | | - - `denied` |
25 | | - - `expired` |
26 | | - - `revoked` |
27 | | -- `approver` — object with: |
28 | | - - `type` |
29 | | - - `id` |
30 | | - - optional `display` |
31 | | -- `scope` — object with: |
32 | | - - `kind` |
33 | | - - `tool` |
34 | | - - `operation` |
35 | | - - optional `resource` |
36 | | -- `time` — object with: |
37 | | - - `requested_at` |
38 | | - - `decided_at` |
39 | | - - optional `expires_at` |
| 17 | +- `approval_id` (string) — unique ID for the approval record |
| 18 | +- `type` (enum) — `human | ticket | breakglass` |
| 19 | +- `status` (enum) — `requested | approved | denied | expired | revoked` |
| 20 | +- `approver` (object) |
| 21 | + - `type` (string) — e.g. `user`, `service`, `group` |
| 22 | + - `id` (string) — stable identifier (email, IAM principal, etc.) |
| 23 | + - `display` (string, optional) |
| 24 | +- `scope` (object) |
| 25 | + - `kind` (string) — e.g. `tool.invoke` |
| 26 | + - `tool` (string) |
| 27 | + - `operation` (string) |
| 28 | + - `resource` (string, optional) — e.g. path, URL, ARN, ticket ID, etc. |
| 29 | +- `time` (object) |
| 30 | + - `requested_at` (date-time) |
| 31 | + - `decided_at` (date-time) |
| 32 | + - `expires_at` (date-time, optional) |
40 | 33 |
|
41 | 34 | Optional fields: |
| 35 | +- `notes` (string) |
| 36 | +- `signature` (object) |
| 37 | + - `alg` (string) — `ed25519` |
| 38 | + - `key_id` (string) |
| 39 | + - `value` (string) — signature over canonical approval payload (RFC8785), including `signature.alg` and `signature.key_id`, but excluding `signature.value` |
42 | 40 |
|
43 | | -- `notes` |
44 | | -- `signature` |
| 41 | +--- |
45 | 42 |
|
46 | | -## Approval signature object |
| 43 | +## Approval signatures (implemented) |
47 | 44 |
|
48 | | -When present, `signature` must be an object with: |
49 | | - |
50 | | -- `alg` |
51 | | -- `key_id` |
52 | | -- `value` |
53 | | - |
54 | | -In the current implementation, approval signing uses the same canonicalization rule as receipt signing: |
55 | | - |
56 | | -- canonical JSON via **RFC8785-JCS** |
57 | | -- sign the approval payload while excluding `signature.value` |
58 | | -- store the signature as base64url text |
59 | | - |
60 | | -## Current implementation behavior |
61 | | - |
62 | | -### What the simulator does |
63 | | - |
64 | | -The simulator can embed a single approval record when `--approve` is provided. |
65 | | - |
66 | | -Example: |
67 | | - |
68 | | -```bash |
69 | | -go run ./cmd/ix-an simulate \ |
70 | | - --path docs/approved.txt \ |
71 | | - --out /tmp/approved.receipt.json \ |
72 | | - --approve \ |
73 | | - --approver you@example.com \ |
74 | | - --approval-type ticket |
75 | | -``` |
76 | | - |
77 | | -In the current demo flow, the simulator signs the approval object using the same signing key used for the receipt. |
78 | | - |
79 | | -### What strict approval verification means |
80 | | - |
81 | | -Use `--strict-approvals` when verifying a receipt: |
| 45 | +### Simulator emits signed approvals |
| 46 | +Run: |
82 | 47 |
|
83 | 48 | ```bash |
84 | | -go run ./cmd/ix-an verify \ |
85 | | - --strict-hashes \ |
86 | | - --strict-signature \ |
87 | | - --strict-approvals \ |
88 | | - /tmp/approved.receipt.json |
89 | | -``` |
90 | | - |
91 | | -Under strict approval verification: |
92 | | - |
93 | | -- if approvals are present, each approval must include a signature |
94 | | -- each approval signature must verify successfully |
95 | | -- malformed or unsigned approvals cause verification failure |
96 | | - |
97 | | -Without `--strict-approvals`, approvals may still be present in the receipt, but the verifier will not require signatures on them. |
98 | | - |
99 | | -## Why approvals matter |
| 49 | +go run ./cmd/ix-an simulate --path docs/approved.txt --out /tmp/approved.receipt.json \ |
| 50 | + --approve --approver you@example.com --approval-type ticket |
100 | 51 |
|
101 | | -Approvals turn receipts into governance artifacts instead of plain execution logs. |
| 52 | +The simulator signs the approval object (demo uses the same key as receipt signing). |
102 | 53 |
|
103 | | -That matters for: |
| 54 | +Verifier can enforce signed approvals |
104 | 55 |
|
105 | | -- SOC 2 and ISO 27001 evidence trails |
106 | | -- change-management linkage |
107 | | -- break-glass event recording |
108 | | -- higher-assurance workflows where risky actions need separate human or ticket authorization |
| 56 | +Run: |
| 57 | +go run ./cmd/ix-an verify /tmp/approved.receipt.json --strict-approvals --strict-hashes --strict-signature |
109 | 58 |
|
110 | | -## Practical interpretation |
| 59 | +Strict approvals means: |
111 | 60 |
|
112 | | -An approval does **not** replace policy. |
113 | | -It complements policy. |
| 61 | +if approvals exist, each approval must include a signature |
114 | 62 |
|
115 | | -The pattern is: |
| 63 | +each signature must verify |
116 | 64 |
|
117 | | -1. policy says whether the action class is even eligible |
118 | | -2. approvals carry governance context for actions that require explicit sign-off |
119 | | -3. the receipt binds policy evidence, approval evidence, and execution evidence into one verifiable record |
| 65 | +Why approvals matter (buyer value) |
120 | 66 |
|
121 | | -## v0 scope |
| 67 | +Approvals turn receipts into auditable governance artifacts: |
122 | 68 |
|
123 | | -Current v0 support is intentionally small: |
| 69 | +SOC2 / ISO27001 evidence |
124 | 70 |
|
125 | | -- one or more structured approval objects in `policy.approvals[]` |
126 | | -- optional approval signatures |
127 | | -- strict verification mode for approval signatures |
| 71 | +change-management linkage (ticket approvals) |
128 | 72 |
|
129 | | -Future enterprise-grade extensions could add: |
| 73 | +break-glass logging (incident-time access) |
130 | 74 |
|
131 | | -- quorum approvals |
132 | | -- separate approval trust domains |
133 | | -- external ticket-system binding |
134 | | -- expiry and revocation workflows tied to policy engines |
| 75 | +least-privilege + “two-person rule” patterns (future extension) |
0 commit comments