@intentius/chant-lexicon-cedar 0.44.8 → 0.44.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/README.md +106 -2
  2. package/dist/agentcore/embed.d.ts +189 -0
  3. package/dist/agentcore/embed.d.ts.map +1 -0
  4. package/dist/agentcore/enforcement.d.ts +76 -0
  5. package/dist/agentcore/enforcement.d.ts.map +1 -0
  6. package/dist/agentcore/scan.d.ts +46 -0
  7. package/dist/agentcore/scan.d.ts.map +1 -0
  8. package/dist/codegen/docs-dogwood.d.ts +21 -0
  9. package/dist/codegen/docs-dogwood.d.ts.map +1 -0
  10. package/dist/codegen/docs.d.ts.map +1 -1
  11. package/dist/codegen/package.d.ts.map +1 -1
  12. package/dist/config.d.ts +25 -0
  13. package/dist/config.d.ts.map +1 -1
  14. package/dist/dogwood/cli.d.ts +247 -0
  15. package/dist/dogwood/cli.d.ts.map +1 -0
  16. package/dist/dogwood/event-schema.d.ts +161 -0
  17. package/dist/dogwood/event-schema.d.ts.map +1 -0
  18. package/dist/dogwood/index.d.ts +39 -0
  19. package/dist/dogwood/index.d.ts.map +1 -0
  20. package/dist/dogwood/macros.d.ts +96 -0
  21. package/dist/dogwood/macros.d.ts.map +1 -0
  22. package/dist/dogwood/policy.d.ts +120 -0
  23. package/dist/dogwood/policy.d.ts.map +1 -0
  24. package/dist/dogwood/replay-activity.d.ts +196 -0
  25. package/dist/dogwood/replay-activity.d.ts.map +1 -0
  26. package/dist/dogwood/replay-op.d.ts +165 -0
  27. package/dist/dogwood/replay-op.d.ts.map +1 -0
  28. package/dist/dogwood/scan.d.ts +109 -0
  29. package/dist/dogwood/scan.d.ts.map +1 -0
  30. package/dist/dogwood/serialize.d.ts +66 -0
  31. package/dist/dogwood/serialize.d.ts.map +1 -0
  32. package/dist/dogwood/temporal.d.ts +259 -0
  33. package/dist/dogwood/temporal.d.ts.map +1 -0
  34. package/dist/dogwood/trace.d.ts +215 -0
  35. package/dist/dogwood/trace.d.ts.map +1 -0
  36. package/dist/dogwood/upstream.d.ts +41 -0
  37. package/dist/dogwood/upstream.d.ts.map +1 -0
  38. package/dist/dogwood/window.d.ts +73 -0
  39. package/dist/dogwood/window.d.ts.map +1 -0
  40. package/dist/index.d.ts +12 -0
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/integrity.json +15 -3
  43. package/dist/lint/audit-catalog.d.ts.map +1 -1
  44. package/dist/lint/post-synth/dogwood-helpers.d.ts +63 -0
  45. package/dist/lint/post-synth/dogwood-helpers.d.ts.map +1 -0
  46. package/dist/lint/post-synth/dwdc010.d.ts +25 -0
  47. package/dist/lint/post-synth/dwdc010.d.ts.map +1 -0
  48. package/dist/lint/post-synth/dwdc011.d.ts +19 -0
  49. package/dist/lint/post-synth/dwdc011.d.ts.map +1 -0
  50. package/dist/lint/post-synth/dwdc012.d.ts +21 -0
  51. package/dist/lint/post-synth/dwdc012.d.ts.map +1 -0
  52. package/dist/lint/post-synth/dwdc013.d.ts +33 -0
  53. package/dist/lint/post-synth/dwdc013.d.ts.map +1 -0
  54. package/dist/lint/post-synth/dwde010.d.ts +32 -0
  55. package/dist/lint/post-synth/dwde010.d.ts.map +1 -0
  56. package/dist/lint/post-synth/dwde011.d.ts +33 -0
  57. package/dist/lint/post-synth/dwde011.d.ts.map +1 -0
  58. package/dist/lint/post-synth/dwds010.d.ts +24 -0
  59. package/dist/lint/post-synth/dwds010.d.ts.map +1 -0
  60. package/dist/lint/post-synth/index.d.ts.map +1 -1
  61. package/dist/manifest.json +1 -1
  62. package/dist/okf/index.md +7 -0
  63. package/dist/okf/rules/DWDC010.md +11 -0
  64. package/dist/okf/rules/DWDC011.md +11 -0
  65. package/dist/okf/rules/DWDC012.md +11 -0
  66. package/dist/okf/rules/DWDC013.md +15 -0
  67. package/dist/okf/rules/DWDE010.md +11 -0
  68. package/dist/okf/rules/DWDE011.md +11 -0
  69. package/dist/okf/rules/DWDS010.md +11 -0
  70. package/dist/okf/types/Policy.md +1 -0
  71. package/dist/op/activities/index.d.ts +18 -0
  72. package/dist/op/activities/index.d.ts.map +1 -0
  73. package/dist/plugin.d.ts.map +1 -1
  74. package/dist/policy-text.d.ts +53 -0
  75. package/dist/policy-text.d.ts.map +1 -0
  76. package/dist/rules/dogwood-helpers.ts +139 -0
  77. package/dist/rules/dwdc010.ts +62 -0
  78. package/dist/rules/dwdc011.ts +61 -0
  79. package/dist/rules/dwdc012.ts +46 -0
  80. package/dist/rules/dwdc013.ts +64 -0
  81. package/dist/rules/dwde010.ts +130 -0
  82. package/dist/rules/dwde011.ts +108 -0
  83. package/dist/rules/dwds010.ts +46 -0
  84. package/dist/serializer.d.ts +10 -18
  85. package/dist/serializer.d.ts.map +1 -1
  86. package/dist/skills/chant-cedar-authoring.md +180 -0
  87. package/dist/skills/chant-cedar-avp-embedding.md +125 -0
  88. package/dist/skills/chant-cedar-dogwood.md +327 -0
  89. package/dist/skills/chant-cedar-meta-policy.md +119 -0
  90. package/package.json +7 -2
  91. package/src/agentcore/embed.test.ts +254 -0
  92. package/src/agentcore/embed.ts +399 -0
  93. package/src/agentcore/enforcement.test.ts +43 -0
  94. package/src/agentcore/enforcement.ts +92 -0
  95. package/src/agentcore/scan.ts +119 -0
  96. package/src/codegen/docs-dogwood.ts +1119 -0
  97. package/src/codegen/docs.ts +66 -1
  98. package/src/codegen/package.ts +3 -2
  99. package/src/config.test.ts +12 -0
  100. package/src/config.ts +28 -0
  101. package/src/dogwood/cli.test.ts +513 -0
  102. package/src/dogwood/cli.ts +666 -0
  103. package/src/dogwood/event-schema.test.ts +218 -0
  104. package/src/dogwood/event-schema.ts +318 -0
  105. package/src/dogwood/index.ts +271 -0
  106. package/src/dogwood/macros.test.ts +104 -0
  107. package/src/dogwood/macros.ts +229 -0
  108. package/src/dogwood/policy.test.ts +94 -0
  109. package/src/dogwood/policy.ts +141 -0
  110. package/src/dogwood/replay-activity.test.ts +481 -0
  111. package/src/dogwood/replay-activity.ts +506 -0
  112. package/src/dogwood/replay-op.ts +242 -0
  113. package/src/dogwood/scan.ts +287 -0
  114. package/src/dogwood/serialize.test.ts +331 -0
  115. package/src/dogwood/serialize.ts +246 -0
  116. package/src/dogwood/temporal.test.ts +272 -0
  117. package/src/dogwood/temporal.ts +592 -0
  118. package/src/dogwood/testdata/custom-kinds.dwschema +17 -0
  119. package/src/dogwood/testdata/default-macros.dw +23 -0
  120. package/src/dogwood/testdata/lowered-read-after-login.json +13 -0
  121. package/src/dogwood/testdata/max-window-raised.dwschema +25 -0
  122. package/src/dogwood/testdata/pinned.dwschema +31 -0
  123. package/src/dogwood/testdata/read-after-login.cedarschema +20 -0
  124. package/src/dogwood/testdata/read-after-login.dw +17 -0
  125. package/src/dogwood/testdata/temporal-policies.dw +53 -0
  126. package/src/dogwood/trace.test.ts +231 -0
  127. package/src/dogwood/trace.ts +471 -0
  128. package/src/dogwood/upstream.ts +41 -0
  129. package/src/dogwood/window.ts +124 -0
  130. package/src/index.ts +76 -0
  131. package/src/lint/audit-catalog.ts +64 -0
  132. package/src/lint/post-synth/dogwood-helpers.ts +139 -0
  133. package/src/lint/post-synth/dwd-post-synth.test.ts +374 -0
  134. package/src/lint/post-synth/dwdc010.ts +62 -0
  135. package/src/lint/post-synth/dwdc011.ts +61 -0
  136. package/src/lint/post-synth/dwdc012.ts +46 -0
  137. package/src/lint/post-synth/dwdc013.ts +64 -0
  138. package/src/lint/post-synth/dwde-post-synth.test.ts +368 -0
  139. package/src/lint/post-synth/dwde010.ts +130 -0
  140. package/src/lint/post-synth/dwde011.ts +108 -0
  141. package/src/lint/post-synth/dwds010.ts +46 -0
  142. package/src/lint/post-synth/index.ts +14 -0
  143. package/src/lint/post-synth/post-synth.test.ts +7 -3
  144. package/src/op/activities/index.ts +27 -0
  145. package/src/plugin.test.ts +3 -2
  146. package/src/plugin.ts +30 -0
  147. package/src/policy-text.ts +128 -0
  148. package/src/serializer.ts +71 -109
  149. package/src/skills/chant-cedar-dogwood.md +327 -0
@@ -0,0 +1,46 @@
1
+ /**
2
+ * DWDS010: an emitted event schema with no pinned field widens every predicate
3
+ *
4
+ * The #1657 verification is specific about this one. `ServiceSchema::defaults()`
5
+ * — what runs when nobody passes `--event-schema` — uses
6
+ * `configuration/event-schemas/pinned.dwschema`, which carries
7
+ * `pin callerPrincipal: principalType(A) = principal` on every kind. Every
8
+ * temporal predicate is therefore correlated to the deciding request's
9
+ * principal, and events from other principals are invisible.
10
+ *
11
+ * Supplying any event schema opts out of that default wholesale. So a schema
12
+ * emitted without a pin does not merely "not add" a correlation: it removes
13
+ * one the policy author very likely assumed, and every `formerly` in the set
14
+ * starts matching other principals' events. That is a legitimate design —
15
+ * cross-principal correlation is the reason to write your own schema — but it
16
+ * is a decision, and a decision nobody can see in a diff is one nobody made.
17
+ *
18
+ * Report-only: chant does not know which the author wanted, only that it
19
+ * should be said out loud. `defaultEventSchema({ pinCallerPrincipal: false })`
20
+ * also stamps the reasoning into the emitted file.
21
+ */
22
+ import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
23
+ import { dogwoodSchemaFiles, readEventSchema } from "../../dogwood/scan";
24
+
25
+ export const dwds010: PostSynthCheck = {
26
+ id: "DWDS010",
27
+ description: "An emitted dogwood event schema pins its temporal predicates to a request-side value",
28
+
29
+ check(ctx: PostSynthContext): PostSynthDiagnostic[] {
30
+ const diagnostics: PostSynthDiagnostic[] = [];
31
+
32
+ for (const schema of dogwoodSchemaFiles(ctx)) {
33
+ const facts = readEventSchema(schema.text);
34
+ if (facts.kinds.length === 0 || facts.hasPin) continue;
35
+ diagnostics.push({
36
+ checkId: "DWDS010",
37
+ severity: "warning",
38
+ message: `Dogwood event schema "${schema.source}" declares no pinned field, so temporal predicates correlate across every principal — wider than upstream's default, which pins callerPrincipal to the deciding request's principal. Add a pinned field, or record why cross-principal correlation is wanted.`,
39
+ entity: schema.source,
40
+ lexicon: schema.lexicon,
41
+ });
42
+ }
43
+
44
+ return diagnostics;
45
+ },
46
+ };
@@ -19,6 +19,12 @@
19
19
  * (#1650) generates typed classes onto exactly this shape, so nothing here
20
20
  * changes when it lands — the guards become typed expressions rather than
21
21
  * the opaque Cedar-expression strings they are today.
22
+ *
23
+ * A third view arrives when the build holds dogwood entities (#1658): `.dw`
24
+ * policy text and its `.dwschema`/`macros.dw` companions, rendered by
25
+ * `./dogwood/serialize.ts` and returned in the same result. Both legs share
26
+ * `./policy-text.ts` rather than a copy of it, because a `.dw` policy's head
27
+ * *is* a Cedar policy's head.
22
28
  */
23
29
  import type { Declarable } from "@intentius/chant/declarable";
24
30
  import type { Serializer } from "@intentius/chant/serializer";
@@ -69,14 +75,7 @@ export interface CedarPolicyProps {
69
75
  */
70
76
  annotations?: Record<string, string>;
71
77
  }
72
- /** Escape a string for a double-quoted Cedar literal. */
73
- export declare function escapeCedarString(value: string): string;
74
- /**
75
- * Derive a policy id from a logical name: `allowAdminRead` → `allow-admin-read`.
76
- * Cedar ids are free-form strings; kebab-case keeps them readable in the
77
- * `@id` annotation and stable across a rename-free refactor.
78
- */
79
- export declare function policyIdFromLogicalName(name: string): string;
78
+ export { conditionStrings, escapeCedarString, getProps, isRecord, policyIdFromLogicalName, renderPolicyHead, renderScope, resolvePolicyId, } from "./policy-text.js";
80
79
  /**
81
80
  * One policy, as `.cedar` text.
82
81
  *
@@ -84,6 +83,9 @@ export declare function policyIdFromLogicalName(name: string): string;
84
83
  * nothing else — `AWS::VerifiedPermissions::Policy` carries its policy as
85
84
  * `Definition.Static.Statement`, a single Cedar policy rather than a set. Two
86
85
  * renderers would be two dialects; there is one.
86
+ *
87
+ * The head comes from `renderPolicyHead`, which the dogwood dialect's `.dw`
88
+ * leg also calls: three surfaces, one rendering of an annotation and a scope.
87
89
  */
88
90
  export declare function renderPolicyText(id: string, props: Record<string, unknown>): string;
89
91
  /** The JSON policy-set envelope, exactly as `cedar-wasm` accepts it. */
@@ -117,16 +119,6 @@ export interface CedarPolicyRecord {
117
119
  /** Props with references resolved, ready for either renderer. */
118
120
  props: Record<string, unknown>;
119
121
  }
120
- /**
121
- * The Cedar id for a policy: an explicit `annotations.id` when the author gave
122
- * one, else derived from the logical name.
123
- *
124
- * This is the only rule that links a chant entity to a policy in a live AVP
125
- * store (#1652) — the observation resolves the same id from the same props and
126
- * matches it against the `@id` annotation the statement carries. Two copies of
127
- * this rule would be a mapping that drifts silently, so there is one.
128
- */
129
- export declare function resolvePolicyId(logicalName: string, props: Record<string, unknown>): string;
130
122
  /**
131
123
  * Every `Cedar::Policy` in a build, with references walked and ids resolved.
132
124
  *
@@ -1 +1 @@
1
- {"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9D,OAAO,KAAK,EAAE,UAAU,EAAoB,MAAM,6BAA6B,CAAC;AAGhF,OAAO,EAAgD,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAY5F,8CAA8C;AAC9C,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AAEjD,gFAAgF;AAChF,eAAO,MAAM,mBAAmB,wBAAwB,CAAC;AAEzD,0EAA0E;AAC1E,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE9C;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAClB,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GACrB;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,KAAK,CAAC;IAAC,EAAE,CAAC,EAAE,KAAK,CAAA;CAAE,GACtC;IAAE,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAAC,EAAE,CAAC,EAAE,KAAK,CAAC;IAAC,EAAE,CAAC,EAAE,KAAK,CAAA;CAAE,GACjD;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAAC,EAAE,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC;AAEvD,+CAA+C;AAC/C,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,qDAAqD;IACrD,SAAS,CAAC,EAAE,UAAU,CAAC;IACvB,qDAAqD;IACrD,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC;AASD,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,QAAQ,EACR,QAAQ,EACR,uBAAuB,EACvB,gBAAgB,EAChB,WAAW,EACX,eAAe,GAChB,MAAM,eAAe,CAAC;AAwBvB;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAWnF;AAID,wEAAwE;AACxE,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC3C,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACtC,aAAa,EAAE,OAAO,EAAE,CAAC;CAC1B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,GAAG,CAAC,EAAE,kBAAkB,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CA4BxF;AAID,yFAAyF;AACzF,MAAM,WAAW,iBAAiB;IAChC,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,EAAE,EAAE,MAAM,CAAC;IACX,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,iBAAiB,EAAE,CAiBzF;AAID,eAAO,MAAM,eAAe,EAAE,UAqD7B,CAAC"}
@@ -0,0 +1,180 @@
1
+ ---
2
+ skill: chant-cedar-authoring
3
+ description: Author Cedar authorization policies as typed chant resources — schema to generated classes to .cedar and JSON outputs
4
+ user-invocable: true
5
+ ---
6
+
7
+ # Cedar Policies as Typed Resources
8
+
9
+ ## What this lexicon covers
10
+
11
+ Cedar is an authorization policy language. Its toolchain validates and evaluates
12
+ policies; it does not help you write them. There are no functions, no modules,
13
+ no loops, and templates carry exactly two slots (`?principal`, `?resource`), so
14
+ teams managing large policy sets generate `.cedar` text with string templating.
15
+ This lexicon replaces that with typed TypeScript.
16
+
17
+ Two artifacts come out of every build:
18
+
19
+ - `<name>.cedar` — the policy text every Cedar evaluator reads (Amazon Verified
20
+ Permissions, cedar-agent, an embedded `cedar-wasm`).
21
+ - `policies.cedar.json` — the same set in the Cedar JSON policy format, beside
22
+ it. This is also the parse source for import.
23
+
24
+ chant is nowhere in either. An emitted policy set is consumed by any evaluator.
25
+
26
+ ## The three steps
27
+
28
+ ### 1. Declare a schema
29
+
30
+ The schema is *your* file, not a global upstream — it is the input codegen
31
+ reads. Write it in Cedar's human-readable syntax and point the config at it:
32
+
33
+ ```typescript
34
+ // chant.config.ts
35
+ import type { ChantConfig } from "@intentius/chant";
36
+ import "@intentius/chant-lexicon-cedar";
37
+
38
+ export default {
39
+ lexicons: ["cedar"],
40
+ cedar: {
41
+ schema: "authz/app.cedarschema",
42
+ validation: { mode: "strict", requireProjectSchema: true },
43
+ },
44
+ } satisfies ChantConfig;
45
+ ```
46
+
47
+ Turn `requireProjectSchema` on once the project has its own schema. Without it a
48
+ missing file silently falls back to the bundled default, and the difference
49
+ between "your entity types" and "somebody else's" is invisible.
50
+
51
+ ### 2. Run generate
52
+
53
+ ```bash
54
+ npx chant generate --lexicon cedar
55
+ ```
56
+
57
+ That produces, per declaration in the schema:
58
+
59
+ | Schema declaration | Generated |
60
+ |---|---|
61
+ | `entity Document in [Folder] = { … }` | `Document` class, `DocumentAttributes` property class, `DocumentUid` template-literal type |
62
+ | `action read appliesTo { … }` | `ReadAction` constant, `ReadContext` property class |
63
+ | — | `Policy` class, `EntityTypeName`, `ActionUid`, `PolicyScope`, `ALL_ACTIONS`, `ALL_ENTITY_TYPES` |
64
+
65
+ `DocumentUid` is `` `App::Document::"${string}"` `` — a typo'd namespace is a
66
+ compile error, not a validation error hours later.
67
+
68
+ ### 3. Write policies
69
+
70
+ ```typescript
71
+ import { Policy, ReadAction, WriteAction, type UserUid } from "@intentius/chant-lexicon-cedar";
72
+
73
+ const archivist: UserUid = 'App::User::"archivist"';
74
+
75
+ export const ownerRead = new Policy({
76
+ effect: "permit",
77
+ principal: { is: "App::User" },
78
+ action: { in: [ReadAction, WriteAction] },
79
+ resource: { is: "App::Document" },
80
+ when: ["resource.owner == principal"],
81
+ annotations: { doc: "Owners always read their own documents." },
82
+ });
83
+
84
+ export const restrictDelete = new Policy({
85
+ effect: "forbid",
86
+ resource: { is: "App::Document" },
87
+ when: ['resource.classification == "confidential"'],
88
+ unless: [`principal == ${archivist}`],
89
+ });
90
+ ```
91
+
92
+ ## Policy props
93
+
94
+ | Prop | Meaning |
95
+ |------|---------|
96
+ | `effect` | `"permit"` or `"forbid"`. Defaults to `permit` |
97
+ | `principal`, `action`, `resource` | Scope constraints. Omit for unconstrained |
98
+ | `when` | Cedar expression strings, one `when { … }` clause each |
99
+ | `unless` | Cedar expression strings, one `unless { … }` clause each |
100
+ | `annotations` | `Record<string, string>`, emitted as `@key("value")` |
101
+
102
+ ### Scope forms
103
+
104
+ | Written | Emitted |
105
+ |---|---|
106
+ | `{}` or omitted | `principal` |
107
+ | `{ eq: X }` | `principal == X` |
108
+ | `{ in: X }` | `principal in X` |
109
+ | `{ in: [X, Y] }` | `principal in [X, Y]` |
110
+ | `{ is: "App::User" }` | `principal is App::User` |
111
+ | `{ is: "App::User", in: X }` | `principal is App::User in X` |
112
+
113
+ `when`/`unless` stay strings. Cedar's expression grammar *is* the policy
114
+ language; typing it in TypeScript is a separate problem, and the escape hatch
115
+ would leak either way.
116
+
117
+ ## Policy ids
118
+
119
+ The id comes from the export's logical name, kebab-cased: `allowAdminRead`
120
+ becomes `@id("allow-admin-read")`. Set `annotations.id` to pin one explicitly —
121
+ worth doing for a policy whose id something downstream references.
122
+
123
+ ## Composites
124
+
125
+ Repeated policy shapes go in a factory, because Cedar has nowhere to put them.
126
+
127
+ ```typescript
128
+ import {
129
+ DeleteAction,
130
+ DenyByDefaultSet,
131
+ OwnerCanManage,
132
+ ReadAction,
133
+ WriteAction,
134
+ } from "@intentius/chant-lexicon-cedar";
135
+
136
+ const docOwner = OwnerCanManage({
137
+ entityType: "App::Document",
138
+ actions: [ReadAction, WriteAction],
139
+ principal: "App::User",
140
+ });
141
+
142
+ const guarded = DenyByDefaultSet({
143
+ policies: [docOwner],
144
+ entityType: "App::Document",
145
+ actions: DeleteAction,
146
+ when: ['resource.classification == "confidential"'],
147
+ unless: ['principal == App::User::"archivist"'],
148
+ });
149
+
150
+ export const [confidentialFloor, documentOwnerGrant] = guarded.all;
151
+ ```
152
+
153
+ `DenyByDefaultSet` returns the `forbid` floor and its members from one call, so
154
+ deleting the floor deletes the grants with it. A `forbid` beats every `permit`
155
+ in the set unconditionally — it is the only construct that survives a later,
156
+ wider grant.
157
+
158
+ ## Checking the result
159
+
160
+ ```bash
161
+ npx chant build # emits both artifacts, runs the CED rules
162
+ npx chant coverage --lexicon cedar # is every schema declaration generated?
163
+ ```
164
+
165
+ The MCP tool `cedar:coverage` answers the other question: which schema entity
166
+ types and actions the *policy set* can reach, which are reachable only from a
167
+ `forbid`, and which no policy touches at all. An entity type nothing covers is
168
+ inert under Cedar's default-deny — either the intent or a hole.
169
+
170
+ ## Things that will bite
171
+
172
+ - **An empty schema validates everything clean.** `checkParseSchema("")`
173
+ succeeds. Set `requireProjectSchema: true`.
174
+ - **`Group` and `Team`-shaped container entities appear in no `appliesTo`.**
175
+ They exist to be `in`, so no policy — not even `permit (principal, action,
176
+ resource)` — resolves to them. `cedar:coverage` reports that honestly rather
177
+ than rounding it up.
178
+ - **A policy naming an entity type outside the schema still parses.** It
179
+ resolves to an empty request envelope and never fires. `cedar:coverage`
180
+ reports it under `inert`.
@@ -0,0 +1,125 @@
1
+ ---
2
+ skill: chant-cedar-avp-embedding
3
+ description: Embed a typed cedar-lexicon policy into an AWS Verified Permissions policy resource instead of a hand-written string
4
+ user-invocable: true
5
+ ---
6
+
7
+ # Cedar Policies Inside Verified Permissions
8
+
9
+ ## The seam
10
+
11
+ Amazon Verified Permissions is one deployment vehicle for Cedar, and chant
12
+ already ships it: `AWS::VerifiedPermissions::Policy` in the aws lexicon carries
13
+ its policy text in a `definition.static.statement` field typed `CedarPolicy` —
14
+ which is to say, `string`.
15
+
16
+ That string is the seam. Everything upstream of it — the schema, the entity
17
+ types, the actions, the scope constraints — is what the cedar lexicon owns.
18
+ Everything downstream — the policy store, the CloudFormation `ApplyOp`, the IAM
19
+ around it — is the aws lexicon's, and already works.
20
+
21
+ ```
22
+ cedar lexicon aws lexicon
23
+ schema → Policy → .cedar text → VerifiedPermissionsPolicy.definition.static.statement
24
+ ```
25
+
26
+ The walk-away test holds on both sides. The emitted `.cedar` file is read by any
27
+ evaluator with no AWS involved; the AVP resource deploys through the same
28
+ CloudFormation path every other AWS resource does.
29
+
30
+ ## The typed handoff
31
+
32
+ `avpPolicyDefinition(name, props)` returns exactly the `Definition` property
33
+ `AWS::VerifiedPermissions::Policy` takes, rendered by the same renderer that
34
+ writes the `.cedar` file — so the deployed policy and the reviewed file cannot
35
+ disagree.
36
+
37
+ ```typescript
38
+ import { Policy, ReadAction, avpPolicyDefinition } from "@intentius/chant-lexicon-cedar";
39
+ import { VerifiedPermissionsPolicy } from "@intentius/chant-lexicon-aws";
40
+
41
+ const ownerReadProps = {
42
+ effect: "permit",
43
+ principal: { is: "App::User" },
44
+ action: { eq: ReadAction },
45
+ resource: { is: "App::Document" },
46
+ when: ["resource.owner == principal"],
47
+ } as const;
48
+
49
+ /** The evaluator-agnostic artifact: this is what lands in the `.cedar` file. */
50
+ export const ownerRead = new Policy(ownerReadProps);
51
+
52
+ /** The AVP deployment view of the same policy. */
53
+ export const ownerReadAvp = new VerifiedPermissionsPolicy({
54
+ PolicyStoreId: policyStore.ref(),
55
+ Definition: avpPolicyDefinition("ownerRead", ownerReadProps, {
56
+ ownership: { stack: "authz", env: "prod" },
57
+ description: "Owners read their own documents.",
58
+ }),
59
+ });
60
+ ```
61
+
62
+ Beside it: `avpStatement()` for the bare string, `avpStatementJSON()` for
63
+ evaluators that take the JSON policy format, and `avpPolicySet(entities)` to
64
+ render a whole build's policies at once, keyed by chant entity name.
65
+
66
+ **The cedar lexicon does not depend on the aws lexicon.** The seam is the data
67
+ shape, which is stable CloudFormation. `examples/avp-embedding/` shows the
68
+ pairing with a plain-object stand-in, because the shipped cedar examples build
69
+ against the cedar serializer alone.
70
+
71
+ ## The lifecycle surface
72
+
73
+ `describeResources()`, `observeAmbient()` and `exportResources()` read a live
74
+ policy store. Point them at one with `CEDAR_AVP_POLICY_STORE_ID` (or
75
+ `CEDAR_AVP_POLICY_STORE_ID_<ENV>`), or a `policyStoreId` prop on a declared
76
+ policy.
77
+
78
+ The link from a chant entity to a live policy is the Cedar `@id` annotation,
79
+ which is derived from the export name (`ownerRead` → `owner-read`) unless
80
+ `annotations.id` overrides it. Rename an export and the observation follows it;
81
+ rename it *and* pin `annotations.id` and the live policy stays matched.
82
+
83
+ ## Rules
84
+
85
+ - **Do not write the statement as prose.** A hand-typed
86
+ `"permit(principal, action, resource);"` in an AVP resource is the exact thing
87
+ this lexicon exists to remove, and the meta-policy wall (see the
88
+ `chant-cedar-meta-policy` skill) fails a bare permit in a prod build.
89
+ - **Do not try to tag an individual policy.** AVP policy *stores* are taggable;
90
+ individual policies are not — `CreatePolicy` has no tag surface and a policy
91
+ has no ARN. chant's per-policy marker therefore rides in the policy
92
+ description, stamped by passing `ownership` to `avpPolicyDefinition` and read
93
+ back by `describeResources`/`exportResources`. Store tags remain the coarse
94
+ channel. The design record, including what the choice costs, is
95
+ `src/avp/OWNERSHIP.md`.
96
+ - **Do not hand-write the description when you want ownership.** Pass
97
+ `ownership` and let the marker be encoded; the description is capped at 150
98
+ characters and the encoder truncates prose rather than the marker, which is
99
+ what keeps a chant-owned policy from silently reading as foreign.
100
+ - **Do not treat an ambient permit as housekeeping.** A permit found in a store
101
+ that no source file declares is a standing grant somebody made outside review.
102
+ It is a security finding. `observeAmbient()` reports the statement and its
103
+ effect and stops short of the verdict — the judgement is yours.
104
+
105
+ ## Non-AVP evaluators
106
+
107
+ AVP is not the only target, and the lexicon does not privilege it. The same
108
+ emitted `.cedar` and `policies.cedar.json` feed:
109
+
110
+ - **cedar-agent** — a standalone Cedar decision service; point it at the policy
111
+ file and the entity store.
112
+ - **An embedded `cedar-wasm`** — the package this lexicon already depends on.
113
+ Load the policy text in-process and call `isAuthorized`.
114
+ - **Cloudflare-style embeddings** — the same file, read at the edge.
115
+
116
+ If the deployment target is anything other than AVP, there is no embedding step
117
+ at all. Emit the files and ship them.
118
+
119
+ ## Cedar-for-Kubernetes
120
+
121
+ The CNCF push includes Cedar as a Kubernetes authorizer, with policies as CRDs.
122
+ Those kinds belong to the k8s lexicon's CRD sources — the same rule that kept
123
+ `helm.cattle.io` out of the k3s lexicon. What the cedar lexicon does there is
124
+ lint the policy *text* embedded in those kinds, which is the pattern the ARGO
125
+ rules already use.