Skip to content

Schema

Unlike every other lexicon, cedar’s interesting spec is not one global upstream. It is your schema.

The pinned upstream here is the Cedar grammar@cedar-policy/cedar-wasm, whose language version is asserted before anything is emitted. The schema is the input.

  1. cedar.schema in chant.config.ts
  2. schema.cedarschema in the project root
  3. the schema bundled with the lexicon

Step 3 exists so generate() has something to read in a fresh clone — a generate step that only works after the user does something is a generate step nothing gates. It is a real application-authorization model, not a stub.

Turn it off once you have your own:

chant.config.ts
import type { ChantConfig } from "@intentius/chant";
import "@intentius/chant-lexicon-cedar";
export default {
lexicons: ["cedar"],
cedar: {
schema: "authz/app.cedarschema",
validation: {
mode: "strict",
warnings: "warn",
requireProjectSchema: true,
},
},
} satisfies ChantConfig;

Without requireProjectSchema, a typo in the path is the difference between “your entity types” and “the bundled default” — and the build succeeds either way.

chant cedar generate writes the typed classes into the project:

  1. cedar.outDir in chant.config.ts, resolved against the project root
  2. src/generated/cedar/ under the project root

Inside the lexicon’s own checkout the project root is the package directory, and the output stays at src/generated/ so the package’s own surface stays whole. Everywhere else the output is the project’s, beside the schema it came from, and nothing is written under node_modules. Import from it:

import { Policy, ReadAction } from "./generated/cedar";

When a project schema resolves (steps 1 or 2 above), chant build emits it beside the policies as dist/schema.cedarschema, named after the source file. That is what CEDE010 validates the policy set against, and what a dogwood bundle carries as --policy-schema. The bundled default is never emitted.

To emit a schema by hand, or under another name, declare one:

import { Schema } from "@intentius/chant-lexicon-cedar";
export const authz = new Schema({ text: schemaText, filename: "authz.cedarschema" });
namespace App {
type Level = Long;
type TagSet = Set<String>;
entity Group = { "name": String };
entity User in [Group] = {
"email": String,
"level": Level,
"manager"?: User,
"roles": TagSet,
};
entity Document = {
"title": String,
"owner": User,
"classification": String,
};
action read, write appliesTo {
principal: [User],
resource: [Document],
context: { "mfa": Bool }
};
}

Common types (type Level = Long) are resolved away by the codegen — real schemas use them, so the resolver collapses them rather than pretending they are absent.

KeyMeaning
schemaPath to the .cedarschema, relative to the project root
outDirWhere chant cedar generate writes, relative to the project root. Default src/generated/cedar
validation.mode"strict". The only mode cedar-wasm 4.12 accepts
validation.warnings"ignore", "warn", or "error" — the validator reports “policy is impossible” here, separately from errors
validation.requireProjectSchemaRefuse to fall back to the bundled default

The namespace is a strictObject, nested levels included: a typo inside validation is a config error, not a silently ignored key.

Cedar’s schema grammar has a human-readable form and a JSON form. The codegen consumes the human-readable one; cedar-wasm converts between them (schemaToJson, schemaToText) if you need the other.

The language version is pinned separately from the package version. A package bump that leaves the language at 4.x cannot change what parses, so CEDAR_WASM_VERSION is what the self-upgrade tooling moves and CEDAR_LANG_VERSION is what generate() asserts at runtime. Both live in src/spec/pin.ts, beside a content pin over the bundled default schema.