@janssenproject/cedarling_wasm 2.4.0 → 2.5.0

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.
package/README.md CHANGED
@@ -1,617 +1,511 @@
1
- # Cedarling WASM
1
+ # Getting Started with Cedarling in a JavaScript App
2
2
 
3
- This module is designed to build cedarling for browser wasm.
3
+ The `@janssenproject/cedarling_wasm` package brings
4
+ [Cedarling](https://docs.jans.io/stable/cedarling/) authorization to JavaScript and
5
+ TypeScript applications. Cedarling evaluates Cedar policies within your
6
+ application's process, whether it runs in a browser, on a server, or at the edge.
7
+ It determines whether a principal or token set may perform an action on a resource.
4
8
 
5
- ## Building
9
+ Use Cedarling to apply consistent access policies, inspect authorization decisions,
10
+ retain decision logs, and make application context available to policy evaluation.
6
11
 
7
- For building we use [`wasm-pack`](https://developer.mozilla.org/en-US/docs/WebAssembly/Rust_to_Wasm) for install you can use command `cargo install wasm-pack`
12
+ ## Installation
8
13
 
9
- Build cedarling in release:
14
+ ### Using the Package Manager
10
15
 
11
- ```bash
12
- wasm-pack build --release --target web
16
+ ```sh
17
+ npm install @janssenproject/cedarling_wasm
13
18
  ```
14
19
 
15
- Build cedarling in dev mode
20
+ Choose the package entry for your application. The package ships Cedarling's
21
+ WebAssembly (`.wasm`) binary; the last column describes how that binary is loaded
22
+ and initialized. Your application's policy archive is configured separately.
16
23
 
17
- ```bash
18
- wasm-pack build --target web --dev
19
- ```
24
+ | Environment | Package Entry | Packaged WASM Loading |
25
+ | -------------------------------------------- | --------------------------------------------------- | ----------------------------------- |
26
+ | Browser applications with supported bundlers | `@janssenproject/cedarling_wasm` | Automatic |
27
+ | Node.js 22, 24, or 26, ESM or CommonJS | `@janssenproject/cedarling_wasm` | Automatic |
28
+ | Cloudflare Workers and Vercel Edge | `@janssenproject/cedarling_wasm/edge` | Bundled as a precompiled module |
29
+ | Other ESM bundlers | `@janssenproject/cedarling_wasm/manual` and `/wasm` | Application-configured WASM loading |
30
+
31
+ Browser applications use an ESM-aware bundler. Follow
32
+ [Other Integration Paths](#other-integration-paths) for edge deployment and
33
+ application-controlled WASM loading.
34
+
35
+ ### Build from Source
36
+
37
+ For local generation, package build, and qualification instructions, see the
38
+ [maintainer guide](https://github.com/JanssenProject/jans/blob/main/jans-cedarling/bindings/cedarling_wasm/js/docs/maintainer.md).
20
39
 
21
- Result files will be in `pkg` folder.
40
+ ## Usage
22
41
 
23
- ## Testing
42
+ ### Initialization
24
43
 
25
- For WASM testing we use `wasm-pack` and it allows to make test in `node`, `chrome`, `firefox`, `safari`. You just need specify appropriate flag.
44
+ Create a Cedarling instance with `init()`. It ensures the package's WASM binary is
45
+ loaded and initialized, then creates the instance using an object or `Map` of
46
+ canonical Cedarling bootstrap properties.
26
47
 
27
- Example for firefox.
48
+ For browser or Node.js ESM applications:
28
49
 
29
- ```bash
30
- wasm-pack test --firefox
50
+ ```ts
51
+ import { init } from "@janssenproject/cedarling_wasm";
52
+
53
+ const config = {
54
+ CEDARLING_APPLICATION_NAME: "task-api",
55
+ CEDARLING_LOG_TYPE: "memory",
56
+ CEDARLING_LOG_TTL: 120,
57
+ };
58
+ let cedarling;
59
+ try {
60
+ cedarling = await init({
61
+ ...config,
62
+ CEDARLING_POLICY_STORE_URI: "https://example.com/policy-store.cjar",
63
+ });
64
+ } catch (error) {
65
+ console.error("Cedarling initialization failed", error);
66
+ throw error;
67
+ }
31
68
  ```
32
69
 
33
- ## Run browser example
70
+ Replace the example URL with your application's policy archive. For Node.js
71
+ CommonJS, use the following import and run the initialization inside an async
72
+ function:
34
73
 
35
- To run example using `index.html` you need execute following steps:
74
+ ```js
75
+ const { init } = require("@janssenproject/cedarling_wasm");
76
+ ```
36
77
 
37
- 1. Build wasm cedarling.
38
- 2. Run webserver using `python3 -m http.server` or any other.
39
- 3. Visit example app [localhost](http://localhost:8000/), on this app you will get log in browser console.
40
- - Also you can try use cedarling with web app using [cedarling_app](http://localhost:8000/cedarling_app.html), using custom bootstrap properties and request.
78
+ The examples below reuse `cedarling`. Run each authorization walkthrough
79
+ separately, with a policy store that matches its entities and attributes.
80
+ Handle initialization failures in your application's startup error handler.
81
+ See the [bootstrap-property reference](https://docs.jans.io/stable/cedarling/reference/cedarling-properties/)
82
+ for configuration values, defaults, and token-validation settings.
41
83
 
42
- ## WASM Usage
84
+ ### Policy Store Sources
43
85
 
44
- After building WASM bindings in folder `pkg` you can find where you can find `cedarling_wasm.js` and `cedarling_wasm.d.ts` where is defined interface for application.
86
+ A Cedar archive (`.cjar`) packages the schema, policies, and trusted-issuer
87
+ configuration. Prepare policies with the
88
+ [Agama Lab Policy Designer](https://cloud.gluu.org/agama-lab/dashboard/policy-designer)
89
+ and follow the [Cedar Archive format](https://docs.jans.io/nightly/cedarling/reference/cedarling-policy-store/#cedar-archive-cjar-format)
90
+ for packaging.
45
91
 
46
- In `index.html` described simple usage of `cedarling wasm` API:
92
+ #### Load a Cedar Archive from a URL
47
93
 
48
- ```js
49
- import { BOOTSTRAP_CONFIG, REQUEST_UNSIGNED } from "/example_data.js"; // Import js objects: bootstrap config and request
50
- import initWasm, { init } from "/pkg/cedarling_wasm.js";
94
+ Set `CEDARLING_POLICY_STORE_URI` as shown in [Initialization](#initialization).
95
+ Cedarling downloads the archive while creating the instance. Use an absolute
96
+ HTTP(S) URL. For browser downloads, use the application's origin or configure the
97
+ archive server's CORS headers to allow the application origin.
98
+
99
+ #### Load a Cedar Archive from Bytes
100
+
101
+ Use `initFromArchiveBytes()` instead of `init()` when your application controls
102
+ the download. This browser example reuses `config` from Initialization and
103
+ fetches an archive through a same-origin application endpoint:
51
104
 
52
- async function main() {
53
- await initWasm(); // Initialize the WebAssembly module
105
+ ```ts
106
+ import { initFromArchiveBytes } from "@janssenproject/cedarling_wasm";
54
107
 
55
- let instance = await init(BOOTSTRAP_CONFIG);
56
- // authorize calls take the request as a JSON string: it crosses the
57
- // JS/WASM boundary as one string copy parsed by serde_json
58
- let result = await instance.authorize_unsigned(JSON.stringify(REQUEST_UNSIGNED));
59
- console.log("result:", result);
108
+ let policyArchiveBytes;
109
+ let cedarling;
110
+ try {
111
+ const response = await fetch("/api/policy-store");
112
+ if (!response.ok) throw new Error("Policy-store download failed");
113
+ policyArchiveBytes = new Uint8Array(await response.arrayBuffer());
114
+ cedarling = await initFromArchiveBytes(config, policyArchiveBytes);
115
+ } catch (error) {
116
+ console.error("Policy-store download or initialization failed", error);
117
+ throw error;
60
118
  }
61
- main().catch(console.error);
62
119
  ```
63
120
 
64
- Before using any function from library you need initialize WASM runtime by calling `initWasm` function.
121
+ The archive bytes supply the policy store; `config` supplies the other bootstrap
122
+ properties. The package still loads and initializes its own WASM binary. In
123
+ Node.js, use an absolute download URL. Keep upstream policy-store credentials on
124
+ a trusted server; browser configuration is visible to its users.
65
125
 
66
- ### Defined API
126
+ ### Authorization
67
127
 
68
- ```ts
69
- /**
70
- * Create a new instance of the Cedarling application.
71
- * This function can take as config parameter the eather `Map` other `Object`
72
- */
73
- export function init(config: any): Promise<Cedarling>;
74
-
75
- /**
76
- * Create a new instance of the Cedarling application from archive bytes.
77
- *
78
- * This function allows loading a policy store from a Cedar Archive (.cjar)
79
- * that was fetched with custom logic (e.g., with authentication headers).
80
- *
81
- * # Arguments
82
- * * `config` - Bootstrap configuration (Map or Object). Policy store config is ignored.
83
- * * `archive_bytes` - The .cjar archive bytes (Uint8Array)
84
- *
85
- * # Example
86
- * ```javascript
87
- * const response = await fetch(url, { headers: { Authorization: 'Bearer ...' } });
88
- * const bytes = new Uint8Array(await response.arrayBuffer());
89
- * const cedarling = await init_from_archive_bytes(config, bytes);
90
- * ```
91
- */
92
- export function init_from_archive_bytes(config: any, archive_bytes: Uint8Array): Promise<Cedarling>;
93
-
94
- /**
95
- * The instance of the Cedarling application.
96
- */
97
- export class Cedarling {
98
- /**
99
- * Create a new instance of the Cedarling application.
100
- * Assume that config is `Object`
101
- */
102
- static new(config: object): Promise<Cedarling>;
103
- /**
104
- * Create a new instance of the Cedarling application.
105
- * Assume that config is `Map`
106
- */
107
- static new_from_map(config: Map<any, any>): Promise<Cedarling>;
108
- /**
109
- * Authorize an unsigned request carrying an optional single principal.
110
- * Makes an authorization decision based on the [`RequestUnsigned`].
111
- * When `principal` is omitted / `null` the core uses Cedar partial evaluation;
112
- * residual-dependent requests fail closed with `Decision::Deny` and surface
113
- * residual policy ids in `response.diagnostics.reason`.
114
- */
115
- authorize_unsigned(request: string): Promise<AuthorizeResult>;
116
- /**
117
- * Authorize multi-issuer request.
118
- * Makes authorization decision based on multiple JWT tokens from different issuers
119
- * The request is passed as a JSON string.
120
- */
121
- authorize_multi_issuer(request: string): Promise<MultiIssuerAuthorizeResult>;
122
- /**
123
- * Get logs and remove them from the storage.
124
- * Returns `Array` of `Map`
125
- */
126
- pop_logs(): Array<any>;
127
- /**
128
- * Get specific log entry.
129
- * Returns `Map` with values or `null`.
130
- */
131
- get_log_by_id(id: string): any;
132
- /**
133
- * Returns a list of all log ids.
134
- * Returns `Array` of `String`
135
- */
136
- get_log_ids(): Array<any>;
137
- /**
138
- * Get logs by tag, like `log_kind` or `log level`.
139
- * Tag can be `log_kind`, `log_level`.
140
- */
141
- get_logs_by_tag(tag: string): any[];
142
- /**
143
- * Get logs by request_id.
144
- * Return log entries that match the given request_id.
145
- */
146
- get_logs_by_request_id(request_id: string): any[];
147
- /**
148
- * Get log by request_id and tag, like composite key `request_id` + `log_kind`.
149
- * Tag can be `log_kind`, `log_level`.
150
- * Return log entries that match the given request_id and tag.
151
- */
152
- get_logs_by_request_id_and_tag(request_id: string, tag: string): any[];
153
- /**
154
- * Push a value into the data store with an optional TTL.
155
- * If the key already exists, the value will be replaced.
156
- * If TTL is not provided, the default TTL from configuration is used.
157
- *
158
- * # Arguments
159
- * * `key` - The key for the data entry
160
- * * `value` - The value to store (any JSON-serializable value)
161
- * * `ttl_secs` - Optional TTL in seconds (undefined uses default from config)
162
- *
163
- * # Example
164
- * ```javascript
165
- * cedarling.push_data_ctx("user:123", { name: "John", age: 30 }, 3600);
166
- * cedarling.push_data_ctx("config", { setting: "value" }); // Uses default TTL
167
- * ```
168
- */
169
- push_data_ctx(key: string, value: any, ttl_secs?: number): void;
170
- /**
171
- * Get a value from the data store by key.
172
- * Returns null if the key doesn't exist or the entry has expired.
173
- *
174
- * # Arguments
175
- * * `key` - The key to retrieve
176
- *
177
- * # Example
178
- * ```javascript
179
- * const value = cedarling.get_data_ctx("user:123");
180
- * if (value) {
181
- * console.log(value.name);
182
- * }
183
- * ```
184
- */
185
- get_data_ctx(key: string): any;
186
- /**
187
- * Get a data entry with full metadata by key.
188
- * Returns null if the key doesn't exist or the entry has expired.
189
- * Includes metadata like creation time, expiration, access count, and type.
190
- *
191
- * # Arguments
192
- * * `key` - The key to retrieve
193
- *
194
- * # Example
195
- * ```javascript
196
- * const entry = cedarling.get_data_entry_ctx("user:123");
197
- * if (entry) {
198
- * console.log(`Created: ${entry.created_at}, Access count: ${entry.access_count}`);
199
- * }
200
- * ```
201
- */
202
- get_data_entry_ctx(key: string): any;
203
- /**
204
- * Remove a value from the data store by key.
205
- * Returns true if the key existed and was removed, false otherwise.
206
- *
207
- * # Arguments
208
- * * `key` - The key to remove
209
- *
210
- * # Example
211
- * ```javascript
212
- * const removed = cedarling.remove_data_ctx("user:123");
213
- * ```
214
- */
215
- remove_data_ctx(key: string): boolean;
216
- /**
217
- * Clear all entries from the data store.
218
- *
219
- * # Example
220
- * ```javascript
221
- * cedarling.clear_data_ctx();
222
- * ```
223
- */
224
- clear_data_ctx(): void;
225
- /**
226
- * List all entries with their metadata.
227
- * Returns an array of data entries containing key, value, type, and timing metadata.
228
- *
229
- * # Example
230
- * ```javascript
231
- * const entries = cedarling.list_data_ctx();
232
- * entries.forEach(entry => {
233
- * console.log(`Key: ${entry.key}, Type: ${entry.data_type}`);
234
- * });
235
- * ```
236
- */
237
- list_data_ctx(): any[];
238
- /**
239
- * Get statistics about the data store.
240
- * Returns current entry count, capacity limits, and configuration state.
241
- *
242
- * # Example
243
- * ```javascript
244
- * const stats = cedarling.get_stats_ctx();
245
- * console.log(`Entries: ${stats.entry_count}/${stats.max_entries}`);
246
- * ```
247
- */
248
- get_stats_ctx(): DataStoreStats;
249
- /**
250
- * Trusted issuer loading status helpers.
251
- */
252
- is_trusted_issuer_loaded_by_name(issuer_id: string): boolean;
253
- is_trusted_issuer_loaded_by_iss(iss_claim: string): boolean;
254
- total_issuers(): number;
255
- loaded_trusted_issuers_count(): number;
256
- loaded_trusted_issuer_ids(): Array<string>;
257
- failed_trusted_issuer_ids(): Array<string>;
258
- }
128
+ Choose token-based authorization when identity comes from JWTs, or unsigned
129
+ authorization when the application supplies a trusted principal. Both methods
130
+ accept a JSON-string request and return a promise that resolves to an authorization
131
+ result containing `decision` and `request_id`.
259
132
 
260
- /**
261
- * A WASM wrapper for the Rust `cedarling::AuthorizeResult` struct.
262
- * Represents the result of an authorization request.
263
- */
264
- export class AuthorizeResult {
265
- /**
266
- * Convert `AuthorizeResult` to json string value
267
- */
268
- json_string(): string;
269
- /**
270
- * Cedar authorization response for the request.
271
- */
272
- response: AuthorizeResultResponse;
273
- /**
274
- * Result of authorization
275
- * true means `ALLOW`
276
- * false means `Deny`
277
- *
278
- * this field is [`bool`] type to be compatible with [authzen Access Evaluation Decision](https://openid.github.io/authzen/#section-6.2.1).
279
- */
280
- decision: boolean;
281
- /**
282
- * Request ID of the authorization request
283
- */
284
- request_id: string;
285
- }
133
+ #### Token-Based Authorization (Multi-Issuer)
286
134
 
287
- /**
288
- * A WASM wrapper for the Rust `cedarling::MultiIssuerAuthorizeResult` struct.
289
- * Represents the result of a multi-issuer authorization request.
290
- */
291
- export class MultiIssuerAuthorizeResult {
292
- /**
293
- * Convert `MultiIssuerAuthorizeResult` to json string value
294
- */
295
- json_string(): string;
296
- /**
297
- * Result of Cedar policy authorization
298
- */
299
- response: AuthorizeResultResponse;
300
- /**
301
- * Result of authorization
302
- * true means `ALLOW`
303
- * false means `Deny`
304
- */
305
- decision: boolean;
306
- /**
307
- * Request ID of the authorization request
308
- */
309
- request_id: string;
310
- }
135
+ Use `authorizeMultiIssuer()` to evaluate tokens from one or more issuers. Your
136
+ policy store must define the token mappings, trusted issuers, resource schema,
137
+ action, and context used below. Configure token validation for those issuers;
138
+ the initialization example retains Cedarling's validation defaults.
311
139
 
312
- /**
313
- * A WASM wrapper for the Rust `cedar_policy::Response` struct.
314
- * Represents the result of an authorization request.
315
- */
316
- export class AuthorizeResultResponse {
317
- /**
318
- * Authorization decision
319
- */
320
- readonly decision: boolean;
321
- /**
322
- * Diagnostics providing more information on how this decision was reached
323
- */
324
- readonly diagnostics: Diagnostics;
325
- }
140
+ 1. Prepare the tokens.
326
141
 
327
- /**
328
- * Diagnostics
329
- * ===========
330
- *
331
- * Provides detailed information about how a policy decision was made, including policies that contributed to the decision and any errors encountered during evaluation.
332
- */
333
- export class Diagnostics {
334
- /**
335
- * `PolicyId`s of the policies that contributed to the decision.
336
- * If no policies applied to the request, this set will be empty.
337
- *
338
- * The ids should be treated as unordered,
339
- */
340
- readonly reason: string[];
341
- /**
342
- * Errors that occurred during authorization. The errors should be
343
- * treated as unordered, since policies may be evaluated in any order.
344
- */
345
- readonly errors: PolicyEvaluationError[];
346
- }
142
+ Obtain `acmeAccessToken` and `dolphinAccessToken` from your application's
143
+ authentication flows. Each `mapping` must match its policy-store token
144
+ metadata; each `payload` is the actual JWT string.
347
145
 
348
- /**
349
- * PolicyEvaluationError
350
- * =====================
351
- *
352
- * Represents an error that occurred when evaluating a Cedar policy.
353
- */
354
- export class PolicyEvaluationError {
355
- /**
356
- * Id of the policy with an error
357
- */
358
- readonly id: string;
359
- /**
360
- * Underlying evaluation error string representation
361
- */
362
- readonly error: string;
363
- }
146
+ ```ts
147
+ const tokens = [
148
+ { mapping: "Acme::Access_Token", payload: acmeAccessToken },
149
+ { mapping: "Dolphin::Access_Token", payload: dolphinAccessToken },
150
+ ];
151
+ ```
364
152
 
365
- /**
366
- * DataStoreStats
367
- * ==============
368
- *
369
- * Statistics about the DataStore, providing insight into the current state
370
- * and usage of the data store, including memory usage metrics and capacity information.
371
- */
372
- export class DataStoreStats {
373
- /**
374
- * Number of entries currently stored
375
- */
376
- readonly entry_count: number;
377
- /**
378
- * Maximum number of entries allowed (0 = unlimited)
379
- */
380
- readonly max_entries: number;
381
- /**
382
- * Maximum size per entry in bytes (0 = unlimited)
383
- */
384
- readonly max_entry_size: number;
385
- /**
386
- * Whether metrics tracking is enabled
387
- */
388
- readonly metrics_enabled: boolean;
389
- /**
390
- * Total size of all entries in bytes (approximate, based on JSON serialization)
391
- */
392
- readonly total_size_bytes: number;
393
- /**
394
- * Average size per entry in bytes (0 if no entries)
395
- */
396
- readonly avg_entry_size_bytes: number;
397
- /**
398
- * Percentage of capacity used (0.0-100.0, based on entry count)
399
- */
400
- readonly capacity_usage_percent: number;
401
- /**
402
- * Memory usage threshold percentage (from config)
403
- */
404
- readonly memory_alert_threshold: number;
405
- /**
406
- * Whether memory usage exceeds the alert threshold
407
- */
408
- readonly memory_alert_triggered: boolean;
409
- }
410
- ```
153
+ 2. Define the resource.
411
154
 
412
- ## Configuration
155
+ This example targets a document. Its type and attributes must match your schema.
413
156
 
414
- ### Policy Store Sources
157
+ ```ts
158
+ const resource = {
159
+ cedar_entity_mapping: { entity_type: "Task::Document", id: "document-1" },
160
+ owner: "alice",
161
+ };
162
+ ```
415
163
 
416
- Cedarling supports multiple ways to load policy stores. **In WASM environments, only URL-based loading is available** (no filesystem access).
164
+ 3. Define the action.
417
165
 
418
- #### WASM-Supported Options
166
+ ```ts
167
+ const action = 'Task::Action::"Read"';
168
+ ```
419
169
 
420
- ```javascript
421
- // Option 1: Fetch policy store from URL (simple)
422
- const BOOTSTRAP_CONFIG = {
423
- CEDARLING_POLICY_STORE_URI: "https://example.com/policy-store.cjar",
424
- // Optional: re-fetch every 60s and atomically swap on change.
425
- // Default is 0 (load-once-at-startup). See "Refreshing the policy store"
426
- // in docs/cedarling/reference/cedarling-properties.md for details.
427
- CEDARLING_POLICY_STORE_REFRESH_INTERVAL: 60,
428
- // ... other config
429
- };
430
- const cedarling = await init(BOOTSTRAP_CONFIG);
431
-
432
- // Option 2: Inline JSON string (for embedded policy stores)
433
- // policyStoreJson is the policy store JSON as a string
434
- // See: https://docs.jans.io/stable/cedarling/reference/cedarling-policy-store/
435
- const policyStoreJson = '{"cedar_version":"4.0","policy_stores":{...}}';
436
- const BOOTSTRAP_CONFIG = {
437
- CEDARLING_POLICY_STORE_LOCAL: policyStoreJson,
438
- // ... other config
439
- };
440
- const cedarling = await init(BOOTSTRAP_CONFIG);
441
-
442
- // Option 3: Custom fetch with auth headers (use init_from_archive_bytes)
443
- const response = await fetch("https://example.com/policy-store.cjar", {
444
- headers: { Authorization: `Bearer ${token}` },
445
- });
446
- const bytes = new Uint8Array(await response.arrayBuffer());
447
- const cedarling = await init_from_archive_bytes(BOOTSTRAP_CONFIG, bytes);
448
- ```
170
+ 4. Define the context.
449
171
 
450
- > **Note:** Directory-based loading and file-based loading are **NOT supported in WASM** (no filesystem access). Use URL-based loading or `init_from_archive_bytes` for custom fetch scenarios.
172
+ Supply request-specific data required by your policies and schema.
451
173
 
452
- #### Cedar Archive (.cjar) Format
174
+ ```ts
175
+ const context = { tenant: "example" };
176
+ ```
453
177
 
454
- For the new directory-based format in WASM, package the directory structure as a `.cjar` file (ZIP archive):
178
+ 5. Build the request.
455
179
 
456
- ```bash
457
- cd policy-store && zip -r ../policy-store.cjar .
458
- ```
180
+ ```ts
181
+ const request = { tokens, action, resource, context };
182
+ ```
459
183
 
460
- See [Policy Store Formats](../../../docs/cedarling/reference/cedarling-policy-store.md#policy-store-formats) for details on the directory structure and metadata.json format.
184
+ 6. Perform authorization and handle the decision.
461
185
 
462
- ### Testing Configuration
186
+ ```ts
187
+ try {
188
+ const result = await cedarling.authorizeMultiIssuer(JSON.stringify(request));
189
+ console.log(result.decision ? "Allowed" : "Denied", result.request_id);
190
+ } catch (error) {
191
+ console.error("Authorization failed", error);
192
+ }
193
+ ```
463
194
 
464
- For testing scenarios, you may want to disable JWT validation. You can configure this in your bootstrap configuration:
195
+ #### Unsigned Authorization
465
196
 
466
- ```javascript
467
- const BOOTSTRAP_CONFIG = {
468
- CEDARLING_JWT_SIG_VALIDATION: "disabled",
469
- CEDARLING_JWT_STATUS_VALIDATION: "disabled",
470
- };
471
- ```
197
+ Use `authorizeUnsigned()` when your application supplies the principal and its
198
+ attributes. Obtain those values from trusted application state. The policy store
199
+ for this example must define `Task::User` with a `role`, `Task::Document` with an
200
+ `owner`, and the `Read` action with the `tenant` context field.
201
+
202
+ 1. Prepare the principal.
203
+
204
+ ```ts
205
+ const principal = {
206
+ cedar_entity_mapping: { entity_type: "Task::User", id: "alice" },
207
+ role: "member",
208
+ };
209
+ ```
210
+
211
+ 2. Define the resource.
472
212
 
473
- For complete configuration documentation, see [cedarling-properties.md](../../../docs/cedarling/cedarling-properties.md) or on [our page](https://docs.jans.io/stable/cedarling/cedarling-properties/).
213
+ ```ts
214
+ const resource = {
215
+ cedar_entity_mapping: { entity_type: "Task::Document", id: "document-1" },
216
+ owner: "alice",
217
+ };
218
+ ```
474
219
 
475
- ## Context Data API
220
+ 3. Define the action.
476
221
 
477
- The Context Data API allows you to push external data into the Cedarling evaluation context, making it available in Cedar policies through the `context.data` namespace.
222
+ ```ts
223
+ const action = 'Task::Action::"Read"';
224
+ ```
478
225
 
479
- ### Push Data
226
+ 4. Define the context.
480
227
 
481
- Store data with an optional TTL (Time To Live):
228
+ ```ts
229
+ const context = { tenant: "example" };
230
+ ```
482
231
 
483
- ```javascript
484
- // Push data without TTL (uses default from config)
485
- cedarling.push_data_ctx("user:123", {
486
- role: ["admin", "editor"],
487
- country: "US"
488
- });
232
+ 5. Build the request.
489
233
 
490
- // Push data with TTL (5 minutes = 300 seconds)
491
- cedarling.push_data_ctx("config:app", { setting: "value" }, 300);
234
+ ```ts
235
+ const request = { principal, action, resource, context };
236
+ ```
492
237
 
493
- // Push different data types
494
- cedarling.push_data_ctx("key1", "string_value");
495
- cedarling.push_data_ctx("key2", 42);
496
- cedarling.push_data_ctx("key3", [1, 2, 3]);
497
- cedarling.push_data_ctx("key4", { nested: "data" });
238
+ 6. Perform authorization and handle the decision.
239
+
240
+ ```ts
241
+ try {
242
+ const result = await cedarling.authorizeUnsigned(JSON.stringify(request));
243
+ console.log(result.decision ? "Allowed" : "Denied", result.request_id);
244
+ } catch (error) {
245
+ console.error("Authorization failed", error);
246
+ }
247
+ ```
248
+
249
+ For both methods, `decision: false` is a valid denial. Malformed requests reject
250
+ the call. Multi-issuer authorization can continue with tokens that pass validation,
251
+ so policies must require the tokens needed for access. Perform the protected
252
+ operation only after receiving `decision: true`; enforce server-side access at the
253
+ server boundary.
254
+
255
+ #### Batch Authorization
256
+
257
+ Evaluate several resources with one principal or token set. Each item contains
258
+ an action, resource, and optional context. Reuse the values from the relevant
259
+ walkthrough:
260
+
261
+ ```ts
262
+ const items = [
263
+ { action, resource, context },
264
+ {
265
+ action,
266
+ resource: {
267
+ ...resource,
268
+ cedar_entity_mapping: { entity_type: "Task::Document", id: "document-2" },
269
+ },
270
+ context,
271
+ },
272
+ ];
273
+ ```
274
+
275
+ For the unsigned walkthrough:
276
+
277
+ ```ts
278
+ let batch;
279
+ try {
280
+ batch = await cedarling.authorizeUnsignedBatch(
281
+ JSON.stringify({ principal, items }),
282
+ );
283
+ } catch (error) {
284
+ console.error("Batch authorization failed", error);
285
+ throw error;
286
+ }
498
287
  ```
499
288
 
500
- ### Get Data
289
+ For the token-based walkthrough, use this call instead:
290
+
291
+ ```ts
292
+ let batch;
293
+ try {
294
+ batch = await cedarling.authorizeMultiIssuerBatch(
295
+ JSON.stringify({ tokens, items }),
296
+ );
297
+ } catch (error) {
298
+ console.error("Batch authorization failed", error);
299
+ throw error;
300
+ }
301
+ ```
501
302
 
502
- Retrieve stored data:
303
+ After a successful batch call, inspect each item in the same order as the
304
+ submitted `items`:
503
305
 
504
- ```javascript
505
- // Get data by key
506
- const value = cedarling.get_data_ctx("user:123");
507
- if (value) {
508
- console.log(`User roles: ${value.role}`);
306
+ ```ts
307
+ for (const item of batch.results) {
308
+ if (item.is_ok) {
309
+ console.log(item.unwrap().decision ? "Allowed" : "Denied");
310
+ } else {
311
+ console.error(item.error?.category, item.error?.message);
312
+ }
509
313
  }
510
314
  ```
511
315
 
512
- ### Get Data Entry with Metadata
316
+ A valid denial has `is_ok === true` and a false decision. A per-item build error
317
+ has `is_ok === false`. The returned `batch_id` correlates the batch's decision logs.
318
+
319
+ ### Logging
513
320
 
514
- Get a data entry with full metadata including creation time, expiration, access count, and type:
321
+ The initialization example enables memory logging and retains entries for up to 120
322
+ seconds. Inside either authorization walkthrough's `try` block, use the returned
323
+ request ID to inspect its logs:
324
+
325
+ ```ts
326
+ const logs = cedarling.getLogsByRequestId(result.request_id);
327
+ console.log(logs);
328
+ ```
515
329
 
516
- ```javascript
517
- const entry = cedarling.get_data_entry_ctx("user:123");
518
- if (entry) {
519
- console.log(`Key: ${entry.key}`);
520
- console.log(`Created at: ${entry.created_at}`);
521
- console.log(`Access count: ${entry.access_count}`);
522
- console.log(`Data type: ${entry.data_type}`);
523
- console.log(`Value:`, entry.value);
330
+ Log records are plain JavaScript objects. To retrieve and clear all retained logs
331
+ when your application is ready to consume them:
332
+
333
+ ```ts
334
+ try {
335
+ const retainedLogs = cedarling.popLogs();
336
+ console.log(retainedLogs);
337
+ } catch (error) {
338
+ console.error("Log retrieval failed", error);
524
339
  }
525
340
  ```
526
341
 
527
- ### Remove Data
342
+ ### Context Data
528
343
 
529
- Remove a specific entry:
344
+ Store application-scoped data for policy evaluation, then retrieve or remove it:
530
345
 
531
- ```javascript
532
- // Remove data by key
533
- const removed = cedarling.remove_data_ctx("user:123");
534
- if (removed) {
535
- console.log("Entry was removed");
536
- } else {
537
- console.log("Entry did not exist");
346
+ ```ts
347
+ try {
348
+ cedarling.pushDataCtx("user:alice", { plan: "pro" }, 3600n);
349
+ const value = cedarling.getDataCtx("user:alice");
350
+ console.log(value);
351
+ cedarling.removeDataCtx("user:alice");
352
+ } catch (error) {
353
+ console.error("Context-data operation failed", error);
538
354
  }
539
355
  ```
540
356
 
541
- ### Clear All Data
357
+ The optional TTL is a `bigint` number of seconds; omitting it uses the configured
358
+ default. Retrieval returns `null` when a value is absent or expired.
359
+
360
+ ### Metrics Snapshot
542
361
 
543
- Remove all entries from the data store:
362
+ Destructive read: returns a metrics snapshot and resets the counters.
544
363
 
545
- ```javascript
546
- cedarling.clear_data_ctx();
364
+ ```ts
365
+ try {
366
+ const snapshot = cedarling.drainMetrics();
367
+ console.log(snapshot.operational_stats.get("authz.requests_total"));
368
+ console.log(snapshot.interval_secs);
369
+ } catch (error) {
370
+ console.error("Metrics snapshot failed", error);
371
+ }
547
372
  ```
548
373
 
549
- ### List All Data
374
+ Requires `CEDARLING_METRICS_COLLECTION: "enabled"`. Fails whenever
375
+ `CEDARLING_LOCK_TELEMETRY_INTERVAL` is set, even if the Lock server has no
376
+ telemetry endpoint. `interval_secs` is a plain `Number` (not `BigInt`), so
377
+ `snapshot.interval_secs + 1` works. It carries fractional seconds, so
378
+ sub-second intervals are reported exactly. Use `snapshot.jsonString()` for a
379
+ JSON-serializable form with plain objects.
550
380
 
551
- List all entries with their metadata:
381
+ ### Trusted Issuer Readiness
552
382
 
553
- ```javascript
554
- const entries = cedarling.list_data_ctx();
555
- entries.forEach(entry => {
556
- console.log(`Key: ${entry.key}, Type: ${entry.data_type}, Created: ${entry.created_at}`);
557
- });
383
+ Before token-based authorization, inspect which configured issuers loaded and
384
+ which failed:
385
+
386
+ ```ts
387
+ console.log("Loaded issuers", cedarling.loadedTrustedIssuerIds());
388
+ console.log("Failed issuers", cedarling.failedTrustedIssuerIds());
558
389
  ```
559
390
 
560
- ### Get Statistics
391
+ To check one issuer, pass its configured identifier to
392
+ `cedarling.isTrustedIssuerLoadedByName(issuerId)`.
561
393
 
562
- Get statistics about the data store:
394
+ ### Policy Annotations
563
395
 
564
- ```javascript
565
- const stats = cedarling.get_stats_ctx();
566
- console.log(`Entries: ${stats.entry_count}/${stats.max_entries}`);
567
- console.log(`Total size: ${stats.total_size_bytes} bytes`);
568
- console.log(`Capacity usage: ${stats.capacity_usage_percent}%`);
396
+ Annotations attach metadata such as descriptions to policies using
397
+ `@key("value")`. They describe policies independently of their authorization rules.
398
+ For example, a policy in your archive can include:
399
+
400
+ ```cedar
401
+ @description("Allows Alice to read document-1")
402
+ permit (
403
+ principal == Task::User::"alice",
404
+ action == Task::Action::"Read",
405
+ resource == Task::Document::"document-1"
406
+ );
569
407
  ```
570
408
 
571
- ### Error Handling
409
+ Inside the unsigned walkthrough's `try` block, retrieve descriptions for the
410
+ determining policy IDs returned in the result:
411
+
412
+ ```ts
413
+ const descriptions = cedarling.annotationValues(
414
+ result.response.diagnostics.reason,
415
+ "description",
416
+ );
417
+ console.log(descriptions);
418
+ ```
572
419
 
573
- The Context Data API methods throw errors for different error conditions:
420
+ See the [Cedar annotation reference](https://docs.cedarpolicy.com/policies/syntax-policy.html#annotations)
421
+ for annotation syntax and behavior.
574
422
 
575
- ```javascript
423
+ ### Shutdown
424
+
425
+ When the application finishes using a Cedarling instance, shut it down:
426
+
427
+ ```ts
576
428
  try {
577
- cedarling.push_data_ctx("", { data: "value" }); // Empty key
429
+ await cedarling.shutDown();
578
430
  } catch (error) {
579
- if (error.message.includes("InvalidKey")) {
580
- console.log("Invalid key provided");
581
- }
582
- }
583
-
584
- const value = cedarling.get_data_ctx("nonexistent");
585
- if (value === null) {
586
- console.log("Key not found");
431
+ console.error("Cedarling shutdown failed", error);
587
432
  }
588
433
  ```
589
434
 
590
- ### Using Data in Cedar Policies
435
+ ### Other Integration Paths
591
436
 
592
- Data pushed via the Context Data API is automatically available in Cedar policies under the `context.data` namespace:
437
+ #### Edge Deployments
593
438
 
594
- ```cedar
595
- permit(
596
- principal,
597
- action == Action::"read",
598
- resource
599
- ) when {
600
- context.data["user:123"].role.contains("admin")
439
+ Use the ESM-only `./edge` entry in hosts whose deployment tool bundles the package's
440
+ WASM binary as a precompiled WebAssembly module. In Cloudflare Workers, initialize inside
441
+ the [request context](https://developers.cloudflare.com/workers/runtime-apis/request/#the-request-context)
442
+ so policy downloads can run:
443
+
444
+ ```ts
445
+ import { init } from "@janssenproject/cedarling_wasm/edge";
446
+
447
+ export default {
448
+ async fetch() {
449
+ let cedarling;
450
+ try {
451
+ cedarling = await init({
452
+ CEDARLING_APPLICATION_NAME: "edge-api",
453
+ CEDARLING_POLICY_STORE_URI: "https://example.com/policy-store.cjar",
454
+ });
455
+ return new Response("Cedarling initialized");
456
+ } catch (error) {
457
+ console.error("Cedarling initialization failed", error);
458
+ return new Response("Internal server error", { status: 500 });
459
+ } finally {
460
+ try {
461
+ await cedarling?.shutDown();
462
+ } catch (error) {
463
+ console.error("Cedarling shutdown failed", error);
464
+ }
465
+ }
466
+ },
601
467
  };
602
468
  ```
603
469
 
604
- The data is injected into the evaluation context before policy evaluation, allowing policies to make decisions based on dynamically pushed data.
470
+ #### Manual Bundler Integration
605
471
 
606
- ## Trusted Issuer Loading Info
472
+ For application-controlled WASM loading, configure your ESM bundler to emit the
473
+ package's `./wasm` export as a URL-addressable binary asset. Configure the server
474
+ to serve it with the `application/wasm` content type. The example below reuses
475
+ `config` and `policyArchiveBytes` from
476
+ [Load a Cedar Archive from Bytes](#load-a-cedar-archive-from-bytes), replacing
477
+ that section's initialization call:
607
478
 
608
- When a policy store contains `trusted-issuers/` entries, you can inspect loading status:
479
+ ```ts
480
+ import initWasm, {
481
+ initFromArchiveBytes,
482
+ } from "@janssenproject/cedarling_wasm/manual";
483
+ import wasmUrl from "@janssenproject/cedarling_wasm/wasm";
609
484
 
610
- ```javascript
611
- const loaded = cedarling.is_trusted_issuer_loaded_by_name("issuer_id");
612
- const loadedByIss = cedarling.is_trusted_issuer_loaded_by_iss("https://issuer.example.org");
613
- const total = cedarling.total_issuers();
614
- const loadedCount = cedarling.loaded_trusted_issuers_count();
615
- const loadedIds = cedarling.loaded_trusted_issuer_ids();
616
- const failedIds = cedarling.failed_trusted_issuer_ids();
485
+ let cedarling;
486
+ try {
487
+ await initWasm({ module_or_path: wasmUrl });
488
+ cedarling = await initFromArchiveBytes(config, policyArchiveBytes);
489
+ } catch (error) {
490
+ console.error("WASM loading or Cedarling initialization failed", error);
491
+ throw error;
492
+ }
617
493
  ```
494
+
495
+ Configure the `.wasm` import to return an asset URL; the exact rule or plugin
496
+ depends on your bundler.
497
+
498
+ ## Upgrading Existing Applications
499
+
500
+ Update calls such as `authorize_unsigned()` to `authorizeUnsigned()`,
501
+ `shut_down()` to `shutDown()`, and `json_string()` to `jsonString()`.
502
+ Bootstrap keys (`CEDARLING_*`), request JSON, result/data fields such as
503
+ `request_id`, and serialized JSON retain their names.
504
+
505
+ ## See Also
506
+
507
+ - [JavaScript tutorial](https://docs.jans.io/stable/cedarling/tutorials/javascript/)
508
+ - [Full API reference](https://docs.jans.io/stable/cedarling/tutorials/javascript/#api-reference)
509
+ - [Bootstrap-property reference](https://docs.jans.io/stable/cedarling/reference/cedarling-properties/)
510
+ - [Cedar Archive format](https://docs.jans.io/nightly/cedarling/reference/cedarling-policy-store/#cedar-archive-cjar-format)
511
+ - [Maintainer guide](https://github.com/JanssenProject/jans/blob/main/jans-cedarling/bindings/cedarling_wasm/js/docs/maintainer.md)