Skip to content

feat(cedarling-js): add JavaScript runtime agnostic sdk for Cedarling. - #14635

Open
Dahkenangnon wants to merge 14 commits into
mainfrom
jans-cedarling-14582
Open

feat(cedarling-js): add JavaScript runtime agnostic sdk for Cedarling.#14635
Dahkenangnon wants to merge 14 commits into
mainfrom
jans-cedarling-14582

Conversation

@Dahkenangnon

@Dahkenangnon Dahkenangnon commented Jul 27, 2026

Copy link
Copy Markdown

Prepare


Description

Target issue

Closes #14582

Implementation Details

This PR introduces @janssenproject/cedarling, a JavaScript and TypeScript SDK wrapping the generated @janssenproject/cedarling_wasm package.

Key changes:

  • Adds a typed, runtime-neutral API for authorization, context management, issuer inspection, decision logs, and client lifecycle management.
  • Supports Node.js, browsers, Bun, Deno, Electron, Cloudflare Workers/workerd, and edge runtimes through runtime-specific package entry points.
  • Adds validation and contract tests that fail early when critical upstream Cedarling WASM exports or response shapes change.
  • Adds unit, contract, browser, packed-consumer, and end-to-end tests across the supported runtimes.
  • Adds runnable React/Node.js, Hono, Electron, and Next.js examples.
  • Excludes bindings/cedarling_js from the parent Rust workspace because it is an npm project built on top of the WASM binding.
  • Integrates the SDK into the existing Cedarling workflows:
    • test-cedarling.yml builds the WASM package locally and runs the complete JavaScript SDK check suite.
    • build-packages.yml shares the generated WASM artifact, builds and validates the SDK package, coordinates release versions, and publishes it to npm using the existing wasm pkg pattern.
  • Adds consumer documentation and a focused maintainer guide covering the SDK boundary, project structure, supported runtimes, testing strategy, build order, and CI release lifecycle.

No new workflow files or npm publishing credentials were introduced.


Test and Document the changes

  • Static code analysis has been run locally and issues have been fixed
  • Relevant unit and integration tests have been added/updated
  • Relevant documentation has been updated

Validation included:

  • npm ci
  • npm run check
  • TypeScript type checking
  • Unit and WASM contract tests
  • Supported-runtime test suites
  • Browser and packed-consumer tests
  • Package build and dry-run publication checks
  • GitHub Actions workflow syntax validation

Documentation is included in the SDK root README and docs/README.md.

  • I confirm that there is no impact on the docs due to the code changes in this PR.

Not applicable: this PR has documentation impact, and the relevant documentation has been updated.

@Dahkenangnon
Dahkenangnon requested a review from moabu as a code owner July 27, 2026 10:15
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Too many files!

This PR contains 265 files, which is 115 over the limit of 150.

To get a review, reduce the PR to 150 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3cefced9-424b-4265-96a3-f268106e4e61

📥 Commits

Reviewing files that changed from the base of the PR and between 69cbfa9 and d62adc3.

⛔ Files ignored due to path filters (8)
  • demos/cedarling-js-examples/common/package-lock.json is excluded by !**/package-lock.json
  • demos/cedarling-js-examples/electron/assets/icon.png is excluded by !**/*.png
  • demos/cedarling-js-examples/electron/package-lock.json is excluded by !**/package-lock.json
  • demos/cedarling-js-examples/hono/package-lock.json is excluded by !**/package-lock.json
  • demos/cedarling-js-examples/react-nodejs/backend/package-lock.json is excluded by !**/package-lock.json
  • demos/cedarling-js-examples/react-nodejs/frontend/package-lock.json is excluded by !**/package-lock.json
  • demos/cedarling-js-examples/vercel-nextjs/package-lock.json is excluded by !**/package-lock.json
  • jans-cedarling/bindings/cedarling_js/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (265)
  • .github/workflows/build-packages.yml
  • .github/workflows/test-cedarling.yml
  • demos/README.md
  • demos/cedarling-js-examples/README.md
  • demos/cedarling-js-examples/common/.gitignore
  • demos/cedarling-js-examples/common/README.md
  • demos/cedarling-js-examples/common/cedarling-config.json
  • demos/cedarling-js-examples/common/idp.js
  • demos/cedarling-js-examples/common/idp.test.js
  • demos/cedarling-js-examples/common/package.json
  • demos/cedarling-js-examples/common/policies/create-token.cedar
  • demos/cedarling-js-examples/common/policies/create-user.cedar
  • demos/cedarling-js-examples/common/policies/modify-token.cedar
  • demos/cedarling-js-examples/common/policies/modify-user.cedar
  • demos/cedarling-js-examples/common/policies/view-token.cedar
  • demos/cedarling-js-examples/common/policies/view-user.cedar
  • demos/cedarling-js-examples/common/policy-store.js
  • demos/cedarling-js-examples/common/schema.cedarschema
  • demos/cedarling-js-examples/common/ui/theme.css
  • demos/cedarling-js-examples/compose.yaml
  • demos/cedarling-js-examples/docker/Dockerfile
  • demos/cedarling-js-examples/docker/Dockerfile.dockerignore
  • demos/cedarling-js-examples/electron/.gitignore
  • demos/cedarling-js-examples/electron/README.md
  • demos/cedarling-js-examples/electron/electron.vite.config.ts
  • demos/cedarling-js-examples/electron/eslint.config.mjs
  • demos/cedarling-js-examples/electron/package.json
  • demos/cedarling-js-examples/electron/postcss.config.js
  • demos/cedarling-js-examples/electron/scripts/docker-workflow.mjs
  • demos/cedarling-js-examples/electron/scripts/docker-workflow.test.mjs
  • demos/cedarling-js-examples/electron/scripts/start-with-docker.mjs
  • demos/cedarling-js-examples/electron/scripts/validate-docker.mjs
  • demos/cedarling-js-examples/electron/scripts/verify-build.mjs
  • demos/cedarling-js-examples/electron/src/__tests__/App.test.tsx
  • demos/cedarling-js-examples/electron/src/__tests__/ipc.test.ts
  • demos/cedarling-js-examples/electron/src/__tests__/oidc.test.ts
  • demos/cedarling-js-examples/electron/src/__tests__/renderer-init.test.ts
  • demos/cedarling-js-examples/electron/src/__tests__/security.test.ts
  • demos/cedarling-js-examples/electron/src/main/cedarling/authorize.ts
  • demos/cedarling-js-examples/electron/src/main/cedarling/config.ts
  • demos/cedarling-js-examples/electron/src/main/cedarling/init.ts
  • demos/cedarling-js-examples/electron/src/main/index.ts
  • demos/cedarling-js-examples/electron/src/main/ipc.ts
  • demos/cedarling-js-examples/electron/src/main/oidc.ts
  • demos/cedarling-js-examples/electron/src/main/security.ts
  • demos/cedarling-js-examples/electron/src/main/tasks.ts
  • demos/cedarling-js-examples/electron/src/preload/electron-api.d.ts
  • demos/cedarling-js-examples/electron/src/preload/index.ts
  • demos/cedarling-js-examples/electron/src/renderer/index.html
  • demos/cedarling-js-examples/electron/src/renderer/src/App.css
  • demos/cedarling-js-examples/electron/src/renderer/src/App.tsx
  • demos/cedarling-js-examples/electron/src/renderer/src/cedarling/init.ts
  • demos/cedarling-js-examples/electron/src/renderer/src/cedarling/permissions.ts
  • demos/cedarling-js-examples/electron/src/renderer/src/index.tsx
  • demos/cedarling-js-examples/electron/src/shared/contracts.ts
  • demos/cedarling-js-examples/electron/tsconfig.app.json
  • demos/cedarling-js-examples/electron/tsconfig.json
  • demos/cedarling-js-examples/hono/.gitignore
  • demos/cedarling-js-examples/hono/README.md
  • demos/cedarling-js-examples/hono/deno.json
  • demos/cedarling-js-examples/hono/package.json
  • demos/cedarling-js-examples/hono/src/app.test.ts
  • demos/cedarling-js-examples/hono/src/app.ts
  • demos/cedarling-js-examples/hono/src/cedarling/authorize.ts
  • demos/cedarling-js-examples/hono/src/cedarling/init.ts
  • demos/cedarling-js-examples/hono/src/entry.bun.ts
  • demos/cedarling-js-examples/hono/src/entry.cloudflare.ts
  • demos/cedarling-js-examples/hono/src/entry.deno.ts
  • demos/cedarling-js-examples/hono/src/tasks.ts
  • demos/cedarling-js-examples/hono/tsconfig.json
  • demos/cedarling-js-examples/hono/wrangler.toml
  • demos/cedarling-js-examples/react-nodejs/.gitignore
  • demos/cedarling-js-examples/react-nodejs/README.md
  • demos/cedarling-js-examples/react-nodejs/backend/.gitignore
  • demos/cedarling-js-examples/react-nodejs/backend/README.md
  • demos/cedarling-js-examples/react-nodejs/backend/cedarling/authz-middleware.js
  • demos/cedarling-js-examples/react-nodejs/backend/cedarling/init.js
  • demos/cedarling-js-examples/react-nodejs/backend/package.json
  • demos/cedarling-js-examples/react-nodejs/backend/server.js
  • demos/cedarling-js-examples/react-nodejs/backend/server.test.js
  • demos/cedarling-js-examples/react-nodejs/frontend/.gitignore
  • demos/cedarling-js-examples/react-nodejs/frontend/README.md
  • demos/cedarling-js-examples/react-nodejs/frontend/docker-entrypoint.sh
  • demos/cedarling-js-examples/react-nodejs/frontend/index.html
  • demos/cedarling-js-examples/react-nodejs/frontend/nginx.conf
  • demos/cedarling-js-examples/react-nodejs/frontend/package.json
  • demos/cedarling-js-examples/react-nodejs/frontend/postcss.config.js
  • demos/cedarling-js-examples/react-nodejs/frontend/src/App.tsx
  • demos/cedarling-js-examples/react-nodejs/frontend/src/cedarling/init.ts
  • demos/cedarling-js-examples/react-nodejs/frontend/src/cedarling/permissions.test.ts
  • demos/cedarling-js-examples/react-nodejs/frontend/src/cedarling/permissions.ts
  • demos/cedarling-js-examples/react-nodejs/frontend/src/index.css
  • demos/cedarling-js-examples/react-nodejs/frontend/src/main.tsx
  • demos/cedarling-js-examples/react-nodejs/frontend/src/model.ts
  • demos/cedarling-js-examples/react-nodejs/frontend/src/oidc.test.ts
  • demos/cedarling-js-examples/react-nodejs/frontend/src/oidc.ts
  • demos/cedarling-js-examples/react-nodejs/frontend/src/task-api.test.ts
  • demos/cedarling-js-examples/react-nodejs/frontend/src/task-api.ts
  • demos/cedarling-js-examples/react-nodejs/frontend/src/vite-env.d.ts
  • demos/cedarling-js-examples/react-nodejs/frontend/tailwind.config.js
  • demos/cedarling-js-examples/react-nodejs/frontend/tsconfig.json
  • demos/cedarling-js-examples/react-nodejs/frontend/vite.config.ts
  • demos/cedarling-js-examples/scripts/install-example.mjs
  • demos/cedarling-js-examples/vercel-nextjs/.gitignore
  • demos/cedarling-js-examples/vercel-nextjs/README.md
  • demos/cedarling-js-examples/vercel-nextjs/app/api/check-edge/route.ts
  • demos/cedarling-js-examples/vercel-nextjs/app/api/check/route.ts
  • demos/cedarling-js-examples/vercel-nextjs/app/api/oidc/callback/route.ts
  • demos/cedarling-js-examples/vercel-nextjs/app/api/oidc/logout/callback/route.ts
  • demos/cedarling-js-examples/vercel-nextjs/app/api/oidc/logout/route.ts
  • demos/cedarling-js-examples/vercel-nextjs/app/api/oidc/session/route.ts
  • demos/cedarling-js-examples/vercel-nextjs/app/api/oidc/start/route.ts
  • demos/cedarling-js-examples/vercel-nextjs/app/api/tasks/[id]/route.ts
  • demos/cedarling-js-examples/vercel-nextjs/app/api/tasks/route.ts
  • demos/cedarling-js-examples/vercel-nextjs/app/globals.css
  • demos/cedarling-js-examples/vercel-nextjs/app/layout.tsx
  • demos/cedarling-js-examples/vercel-nextjs/app/page.tsx
  • demos/cedarling-js-examples/vercel-nextjs/app/task-api.ts
  • demos/cedarling-js-examples/vercel-nextjs/libs/cedarling/authorize.ts
  • demos/cedarling-js-examples/vercel-nextjs/libs/cedarling/init.ts
  • demos/cedarling-js-examples/vercel-nextjs/libs/demo-domain.ts
  • demos/cedarling-js-examples/vercel-nextjs/libs/oidc/auth.ts
  • demos/cedarling-js-examples/vercel-nextjs/libs/oidc/provider.ts
  • demos/cedarling-js-examples/vercel-nextjs/libs/oidc/session.ts
  • demos/cedarling-js-examples/vercel-nextjs/libs/oidc/verify.ts
  • demos/cedarling-js-examples/vercel-nextjs/libs/permission-check.ts
  • demos/cedarling-js-examples/vercel-nextjs/libs/tasks.ts
  • demos/cedarling-js-examples/vercel-nextjs/next-env.d.ts
  • demos/cedarling-js-examples/vercel-nextjs/next.config.ts
  • demos/cedarling-js-examples/vercel-nextjs/package.json
  • demos/cedarling-js-examples/vercel-nextjs/playwright.config.ts
  • demos/cedarling-js-examples/vercel-nextjs/postcss.config.mjs
  • demos/cedarling-js-examples/vercel-nextjs/tests/e2e/oidc.spec.ts
  • demos/cedarling-js-examples/vercel-nextjs/tsconfig.json
  • docker-jans-configurator/Dockerfile
  • docker-jans-configurator/scripts/bootstrap.py
  • docker-jans-persistence-loader/Dockerfile
  • docker-jans-persistence-loader/scripts/utils.py
  • jans-cedarling/Cargo.toml
  • jans-cedarling/bindings/cedarling_js/.gitignore
  • jans-cedarling/bindings/cedarling_js/.npmrc
  • jans-cedarling/bindings/cedarling_js/README.md
  • jans-cedarling/bindings/cedarling_js/docs/README.md
  • jans-cedarling/bindings/cedarling_js/package.json
  • jans-cedarling/bindings/cedarling_js/scripts/assert-publishable.mjs
  • jans-cedarling/bindings/cedarling_js/scripts/clean.mjs
  • jans-cedarling/bindings/cedarling_js/scripts/stage-release.mjs
  • jans-cedarling/bindings/cedarling_js/scripts/verify-consumer.mjs
  • jans-cedarling/bindings/cedarling_js/src/authorization/request.ts
  • jans-cedarling/bindings/cedarling_js/src/authorization/types.ts
  • jans-cedarling/bindings/cedarling_js/src/client/client.ts
  • jans-cedarling/bindings/cedarling_js/src/client/types.ts
  • jans-cedarling/bindings/cedarling_js/src/configuration/prepare.ts
  • jans-cedarling/bindings/cedarling_js/src/configuration/types.ts
  • jans-cedarling/bindings/cedarling_js/src/context/input.ts
  • jans-cedarling/bindings/cedarling_js/src/context/types.ts
  • jans-cedarling/bindings/cedarling_js/src/engine/edge.ts
  • jans-cedarling/bindings/cedarling_js/src/engine/engine.ts
  • jans-cedarling/bindings/cedarling_js/src/engine/factory.ts
  • jans-cedarling/bindings/cedarling_js/src/engine/generated.ts
  • jans-cedarling/bindings/cedarling_js/src/engine/node.ts
  • jans-cedarling/bindings/cedarling_js/src/engine/wasm-modules.d.ts
  • jans-cedarling/bindings/cedarling_js/src/engine/web.ts
  • jans-cedarling/bindings/cedarling_js/src/engine/workerd.ts
  • jans-cedarling/bindings/cedarling_js/src/entries/edge.ts
  • jans-cedarling/bindings/cedarling_js/src/entries/node.ts
  • jans-cedarling/bindings/cedarling_js/src/entries/workerd.ts
  • jans-cedarling/bindings/cedarling_js/src/errors/errors.ts
  • jans-cedarling/bindings/cedarling_js/src/errors/types.ts
  • jans-cedarling/bindings/cedarling_js/src/helpers/constants.ts
  • jans-cedarling/bindings/cedarling_js/src/helpers/records.ts
  • jans-cedarling/bindings/cedarling_js/src/helpers/validation.ts
  • jans-cedarling/bindings/cedarling_js/src/index.ts
  • jans-cedarling/bindings/cedarling_js/src/issuers/input.ts
  • jans-cedarling/bindings/cedarling_js/src/issuers/types.ts
  • jans-cedarling/bindings/cedarling_js/src/logs/normalize.ts
  • jans-cedarling/bindings/cedarling_js/src/logs/query.ts
  • jans-cedarling/bindings/cedarling_js/src/logs/types.ts
  • jans-cedarling/bindings/cedarling_js/src/values/snapshot.ts
  • jans-cedarling/bindings/cedarling_js/src/values/types.ts
  • jans-cedarling/bindings/cedarling_js/tests/.browser.sh
  • jans-cedarling/bindings/cedarling_js/tests/.bun.sh
  • jans-cedarling/bindings/cedarling_js/tests/.deno.sh
  • jans-cedarling/bindings/cedarling_js/tests/.edge.sh
  • jans-cedarling/bindings/cedarling_js/tests/.electron.sh
  • jans-cedarling/bindings/cedarling_js/tests/.workerd.sh
  • jans-cedarling/bindings/cedarling_js/tests/build-sandbox.mjs
  • jans-cedarling/bindings/cedarling_js/tests/contract/authorization.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/contract/authorize-multi-issuer.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/contract/authorize-unsigned.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/contract/configuration.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/contract/context.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/contract/error-contract.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/contract/exports.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/contract/index.ts
  • jans-cedarling/bindings/cedarling_js/tests/contract/issuers.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/contract/lifecycle.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/contract/logs.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/contract/policy-archive.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/contract/policy-loader.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/contract/policy-url.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/contract/value-validation.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/contract/web-entry.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/contract/web-initialization-errors.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/contract/web-native-policy-sources.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/contract/web-tracer.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/e2e/browser-tracer.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/e2e/consumer.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/e2e/index.ts
  • jans-cedarling/bindings/cedarling_js/tests/e2e/signature-validation.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/e2e/stage-release.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/e2e/webpack-build.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/fixtures/browser-tracer-consumer.mjs
  • jans-cedarling/bindings/cedarling_js/tests/fixtures/browser-tracer.html
  • jans-cedarling/bindings/cedarling_js/tests/fixtures/fixtures.ts
  • jans-cedarling/bindings/cedarling_js/tests/fixtures/multi-issuer-policy-store.ts
  • jans-cedarling/bindings/cedarling_js/tests/fixtures/signed-issuer.ts
  • jans-cedarling/bindings/cedarling_js/tests/fixtures/tracer-policy-store.cjar
  • jans-cedarling/bindings/cedarling_js/tests/fixtures/tracer-policy-store.ts
  • jans-cedarling/bindings/cedarling_js/tests/fixtures/webpack.mjs
  • jans-cedarling/bindings/cedarling_js/tests/playwright.config.ts
  • jans-cedarling/bindings/cedarling_js/tests/run-edge.mjs
  • jans-cedarling/bindings/cedarling_js/tests/run.ts
  • jans-cedarling/bindings/cedarling_js/tests/runners/assets.d.ts
  • jans-cedarling/bindings/cedarling_js/tests/runners/browser-page.ts
  • jans-cedarling/bindings/cedarling_js/tests/runners/browser.ts
  • jans-cedarling/bindings/cedarling_js/tests/runners/bun.ts
  • jans-cedarling/bindings/cedarling_js/tests/runners/deno.ts
  • jans-cedarling/bindings/cedarling_js/tests/runners/edge.ts
  • jans-cedarling/bindings/cedarling_js/tests/runners/electron.ts
  • jans-cedarling/bindings/cedarling_js/tests/runners/file-url-fetch-shim.ts
  • jans-cedarling/bindings/cedarling_js/tests/runners/node.ts
  • jans-cedarling/bindings/cedarling_js/tests/runners/portable-suites.ts
  • jans-cedarling/bindings/cedarling_js/tests/runners/sandbox-fixtures.ts
  • jans-cedarling/bindings/cedarling_js/tests/runners/tracer-archive.ts
  • jans-cedarling/bindings/cedarling_js/tests/runners/workerd.ts
  • jans-cedarling/bindings/cedarling_js/tests/tsconfig.json
  • jans-cedarling/bindings/cedarling_js/tests/unit/archive-policy.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/unit/authorize-multi-issuer.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/unit/authorize-unsigned.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/unit/client-errors.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/unit/common-values.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/unit/context.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/unit/edge-initialization.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/unit/engine-boundary.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/unit/engine-fixture.ts
  • jans-cedarling/bindings/cedarling_js/tests/unit/engine-initialization-fixture.ts
  • jans-cedarling/bindings/cedarling_js/tests/unit/errors.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/unit/helpers-validation.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/unit/index.ts
  • jans-cedarling/bindings/cedarling_js/tests/unit/issuers.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/unit/lifecycle.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/unit/loader-policy.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/unit/logs.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/unit/node-initialization.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/unit/options.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/unit/package-entry.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/unit/publishable-manifest.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/unit/url-policy.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/unit/value-inspect.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/unit/wasm-producer-contract.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/unit/web-initialization.test.ts
  • jans-cedarling/bindings/cedarling_js/tests/unit/workerd-initialization.test.ts
  • jans-cedarling/bindings/cedarling_js/tsconfig.build.json
  • jans-cedarling/bindings/cedarling_js/tsconfig.json

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jans-cedarling-14582

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mo-auto

mo-auto commented Jul 27, 2026

Copy link
Copy Markdown
Member

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@mo-auto mo-auto added area-documentation Documentation needs to change as part of issue or PR comp-docs Touching folder /docs comp-jans-cedarling Touching folder /jans-cedarling kind-feature Issue or PR is a new feature request labels Jul 27, 2026
@Dahkenangnon
Dahkenangnon force-pushed the jans-cedarling-14582 branch from b986a7b to fb8af58 Compare July 27, 2026 13:29
@olehbozhok

Copy link
Copy Markdown
Contributor

coderabbit review

@duttarnab duttarnab left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. There should be a section for build from source under installation:
    https://github.com/JanssenProject/jans/blob/jans-cedarling-14582/jans-cedarling/bindings/cedarling_js/README.md#installation

  2. Why this extra dangerouslyDisableSignatureValidation is required we already have bootstrap properties to control validation checks? Also logging.type is not required.

  3. How addition principal and resource attributes will be added .
    https://github.com/JanssenProject/jans/blob/jans-cedarling-14582/jans-cedarling/bindings/cedarling_js/README.md#clientauthorizeunsignedrequest

  4. How to access complete authz-result json containing diagnostics , reason , errors , 'request_id'.

  5. Describe about the test cases, how to run test cases for unsigned and multi-issuer.

  6. Regarding initialization, define how the other bootstrap properties will be defined in args for initialization.

@Dahkenangnon

Dahkenangnon commented Jul 29, 2026

Copy link
Copy Markdown
Author

Thanks for the review. I’ll update the README to address all five points and clarify how the JavaScript SDK maps to Cedarling’s existing bootstrap configuration.

  1. Build from source: Acknowledged. I’ll add a “Build from source” subsection under Installation, including prerequisites, building cedarling_wasm first, installing dependencies, building the SDK, and running the complete validation suite.

  2. Validation and logging configuration: jwt.dangerouslyDisableSignatureValidation is the typed JavaScript mapping for the existing CEDARLING_JWT_SIG_VALIDATION bootstrap property; it does not introduce another validation mechanism. Signature validation is enabled by default, so I’ll remove the redundant false value from the introductory example.

    Similarly, logging.type maps to CEDARLING_LOG_TYPE. The current mappings are offoff, memorymemory, and consolestd_out; Lock integration is configured separately through the SDK’s lock options. Cedarling’s four underlying logging modes are documented in the [Cedarling logging reference](https://docs.jans.io/nightly/cedarling/reference/cedarling-logs/#cedarling-audit-logs). Logging is optional, so I’ll move it out of the minimal initialization example and explain that memory logging is required only when using client.logs.

  3. Principal and resource attributes: Both principal and resource entities accept an attributes object:

    principal: {
      type: "TaskApp::User",
      id: "alice",
      attributes: {
        role: "editor",
        department: "engineering",
      },
    },
    resource: {
      type: "TaskApp::Task",
      id: "task_101",
      attributes: {
        ownerId: "alice",
        status: "open",
      },
    },

    I’ll add this example and explain that these attributes must conform to the policy-store schema. In multi-issuer authorization, principal attributes are derived from mapped token claims, while resource attributes are supplied in the request.

  4. Complete authorization result: The complete normalized result is available through authResult.value:

    if (authResult.ok) {
      const {
        decision,
        requestId,
        diagnostics: { reasons, errors },
      } = authResult.value;
    }

    The SDK uses JavaScript naming conventions: request_id becomes requestId, reason becomes reasons, and diagnostic { id, error } entries become { policyId, message }. I’ll document this result shape and show how to serialize it with JSON.stringify(authResult.value). The raw WASM response remains internal to preserve the SDK’s stable API boundary.

  5. Unsigned and multi-issuer tests: I’ll describe what the unit and real-WASM contract tests cover and add the focused commands:

    npm run test:unit -- authorize-unsigned
    npm run test:contract -- authorize-unsigned
    
    npm run test:unit -- authorize-multi-issuer
    npm run test:contract -- authorize-multi-issuer
    
    npm run check

    I’ll also clarify that the current multi-issuer contract tests use signed synthetic JWTs but disable external signature and status validation for deterministic offline execution. If end-to-end JWKS signature verification is required, that should be covered by an additional dedicated test.

  6. Initialization and remaining bootstrap properties: Acknowledged. I’ll add a configuration reference showing how the supported bootstrap properties are expressed through createCedarling() arguments, including their defaults and mappings. This will cover applicationName, policyStore, logging, authorization, jwt, contextStore, tokenCache, issuerLoading, http, and lock.

    For example:

    • applicationNameCEDARLING_APPLICATION_NAME
    • logging.*CEDARLING_LOG_*
    • jwt.*CEDARLING_JWT_*
    • authorization.* → schema-validation and decision-log properties
    • contextStore.*CEDARLING_DATA_STORE_*
    • tokenCache.*CEDARLING_TOKEN_CACHE_*
    • issuerLoading.*CEDARLING_TRUSTED_ISSUER_LOADER_*
    • http.*CEDARLING_HTTP_*
    • lock.*CEDARLING_LOCK_*

    The SDK intentionally uses typed JavaScript options instead of accepting unrestricted raw bootstrap objects. I’ll also identify any bootstrap properties that are unsupported or not applicable to WebAssembly runtimes, so consumers know exactly what can be configured during initialization.

@Dahkenangnon

Copy link
Copy Markdown
Author

These above will be implement with the following rules:

  • if really consumer facing, into the cedarling/README.md
  • if maintainer or developer facing, will go into cedarling_js/docs/README.md
    Because the reason of the existence of this sdk is to hide the internal dance of the wasm module while offering javascript native interfaces to the js ecosystem dev.

EX: Building from source will be in docs/, while the fix related to the jwt.dangerouslyDisableSignatureValidation and complete authz results in the root readme.

Does it sounds correct ?

@faisalsiddique4400 faisalsiddique4400 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking

Signature validation is untested, and the shipped example disables it

Every contract test that runs real WASM authorization disables it:

The rationale (deterministic offline runs) is already acknowledged in the thread. The gap that remains: if a change stopped verifying signatures, no test in this suite would fail.

The demo IdP at common/idp.js:115 already generates an RSA keypair and serves JWKS, so a valid-signature case and a tampered-signature case can both run offline.

The same defect appears at runtime: common/test-config.json:10-11 sets dangerouslyDisableSignatureValidation and dangerouslyDisableStatusValidation for every example, so the demos' "signed" path accepts forged tokens. Tests don't verify, and the shipped example turns verification off — those should be fixed together.

The publish job cannot authenticate

build_cedarling_js runs npm publish at build-packages.yml:750 with no NODE_AUTH_TOKEN, no .npmrc, and no registry-url: on setup-node at :622. The sibling build_cedarling_wasm job sets it at :445.

The job therefore depends entirely on npm OIDC trusted publishing, hence npm@11.9.0 at :629. Trusted publishing must be configured against a package that already exists on npm; @janssenproject/cedarling does not. The first release will fail until someone publishes manually and configures the trusted publisher for this exact workflow and job name.

The PR description states the SDK is published "using the existing npm publishing secret." No secret is referenced in the job — either the description or the implementation needs correcting.

npm ci runs against a swapped-in file: dependency

build-packages.yml:638 runs npm version "${WASM_VERSION}" after the downloaded artifact has replaced cedarling_wasm/pkg, then :687 runs npm ci against the committed lockfile. dependencies pins file:../cedarling_wasm/pkg, and npm validates linked-package versions against the lock, so a version-bumped artifact is likely to produce "package.json and package-lock.json are not in sync."

This passes in test-cedarling.yml only because wasm-pack there builds the unbumped Cargo.toml version. The workflow_dispatch dry run noted under Release path would confirm or rule this out.

Three call sites reject where their siblings do not

These re-throw when the caught error is not a recognized SDK code:

The issuer, context, and log helpers handle the same case by wrapping. See client.ts:302-315, which uses isSdkErrorCode(...) ? error : createSdkError(...).

The doc comment indicates the throw is intentional for unexpected failures. The issue is the asymmetry: authorize, close, and createCedarling can reject while structurally identical methods never do, and the API shape leads callers to check result.ok and stop there. That produces an unhandled rejection in production.

README.md:366 states the SDK "does not throw errors on expected operational failures". Accurate as written, but "expected" is load-bearing and easy to miss. Either wrap consistently or document the rejection path.

authz-middleware.js — unguarded dereference and an ownership fallback

authz-middleware.js:88 reads diagnostics.reasons unguarded, while :72 correctly guards the same value with if (!decision && diagnostics). A deny without diagnostics throws inside the error path.

:13 sets taskOwner = task ? task.owner : userId, so a missing resource defaults ownership to the caller and any owner-based policy allows. With x-user-id defaulting to 'bob' at :10, and signature validation off per the section above, this is the file consumers are most likely to copy. It needs an explicit "intentionally insecure, do not copy" banner at minimum.

Non-blocking

The guard at assert-publishable.mjs:28 checks name === "cedarling_wasm", but the dependency is @janssenproject/cedarling_wasm, so the branch never executes. Returning false would also mark it unsafe, which inverts the apparent intent. Staging rewrites the version first, so nothing breaks today.

With allowedAlgorithms omitted, all twelve algorithms are enabled: prepare.ts:24-36, applied at prepare.ts:503. none is absent, so this is not urgent. Defaulting to symmetric HS* alongside RS/ES/PS is the standard setup for algorithm confusion. Asymmetric by default, with HS* opt-in, would be safer.

prepare.ts:21 defines UINT32_MAX as "the largest integer representable by the generated unsigned 32-bit fields", but five fields bound to Number.MAX_SAFE_INTEGER instead — CEDARLING_DATA_STORE_MAX_TTL and _DEFAULT_TTL at :437-451, CEDARLING_HTTP_MAX_RESPONSE_SIZE_BYTES at :618, and the three CEDARLING_LOCK_*_INTERVAL values. Validation accepts values the WASM boundary will reject or wrap.

httpUrl() at prepare.ts:177 rejects credentials in the URL but accepts plaintext http:. It gates policyStore.url and lock.configurationUrl, the latter alongside CEDARLING_LOCK_SSA_JWT. Requiring https: outside loopback would match the algorithm-default reasoning above.

withAuthorizationShortcuts at client.ts:80 sets denied: false on the error branch, so if (result.denied) reject() fails open while if (!result.allowed) is safe. Since the shortcuts exist to encourage terse call sites, that asymmetry belongs in the type's doc comment.

selectAuthorizationRequest in authorization/dispatch.ts reads type and request and silently ignores unknown envelope fields, while every other boundary in the SDK calls rejectUnknown.

SDK and WASM versions are permanently coupled: build-packages.yml:680 runs npm version "${WASM_VERSION}" and stage-release.mjs:110 pins exactly. A WASM patch therefore forces an SDK republish, and the published cedarling_wasm version stops matching the Rust crate. Confirming this is the intended long-term behaviour would be useful.

query.ts:94 is unreachable. Given the present === 0 check above it and the id !== undefined early return, requestId and tag cannot both be undefined at that point.

stage-release.mjs:120 spreads ...sourceManifest, so all 10 devDependencies (electron, playwright, webpack, workerd) and 24 test:* scripts land in the published package.json. Consumers do not install devDeps, so the effect is cosmetic.

test-config.json declares expectedFailures: ["authorizeMultiIssuer"] described as a "known bug", echoed by a console.log in the demo's init.js. A linked tracking issue would be better than prose in a fixture.

The new cedarling_js_tests job builds the WASM crate and installs Bun, Deno, and three Playwright browsers to run eight runtime suites, with no timeout-minutes and no persist-credentials: false on checkout. The latter is set on the new build-packages.yml job.

Release path

build-packages.yml triggers on workflow_run and workflow_dispatch only, never on pull_request. The 257 lines of publish logic added here (version coordination, manifest staging, tarball verification, npm publish) have not executed. The green checks on this PR come from test-cedarling.yml. A workflow_dispatch dry run against nightly would validate it — and would settle the npm ci and npm-authentication questions above — before anything depends on it.

Note that the new SDK publish at build-packages.yml:750 fails loudly, whereas the existing WASM publish at line 501 continues on error. Since build_cedarling_js depends on that job and then polls npm view for five minutes, a silently failed WASM publish surfaces here as a confusing timeout.

Minor: the new collect-cedarling-js-digests tag step at :1977 interpolates inputs.target_tag and workflow_run.head_branch directly into run:. This matches the existing pattern used by the other collect-* jobs in the file, so it is not a regression, but build_cedarling_js itself uses the safer env: form and the new job could match it.

CodeRabbit skipped this PR ("249 files exceed the limit of 150"). With 29,181 lines of committed lockfiles and an Electron demo carrying its own webpack configs, the PR exceeds the bots' limits. Splitting the SDK from the demos would restore that coverage.

Checked, no findings

Publish security — injection and secret handling: actions are SHA-pinned, harden-runner present, persist-credentials: false, github.repository fork guard, --provenance with OIDC, SLSA provenance job, WASM package name and version asserted before publish. No injection or token-leak paths found. (Authentication is covered separately under Blocking.)

No committed secrets. The demo IdP generates its keypair at runtime with generateKeyPairSync. The Electron demo sets contextIsolation: true, sandbox: true, nodeIntegration: false, and routes external URLs through shell.openExternal.

Error model: errors.ts:229 produces frozen Error objects, strips URL credentials, and gates retained causes through a WeakSet allowlist. One trade-off: every message becomes a fixed generic string, so the underlying Cedar and WASM detail is discarded. This may make production debugging harder than expected; a debug flag preserving detail would address it.

Input validation is the strongest part of the SDK: inspectOwnProperty avoids invoking getters, rejectUnknown catches misspelled options deterministically, and values are snapshotted before crossing the WASM boundary. Lifecycle handling — in-flight accounting, a single memoized close promise, drain-before-shutdown — is careful. scripts/verify-consumer.mjs packs, installs offline into a scratch consumer, asserts the WASM dependency hoists rather than nests, and exercises both ESM and CJS entry points.

Archive bytes are copied during validation at prepare.ts:302, so the factory does not need to copy again. The SDK lockfile is committed so npm ci resolves in CI. The Cargo workspace exclusion is harmless, though bindings/cedarling_js contains no Cargo.toml, so the entry is a no-op.

```ts
import { createCedarling } from "@janssenproject/cedarling";

const result = await createCedarling({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better to avoid custom mapping to Bootstrap properties. Any changes in the core library would require corresponding updates in this library, which increases the maintenance burden.

I also like that every binding follows the same approach, with the official documentation serving as the single source of truth. Thanks for all the work you've put into this—it's a significant effort. That said, I think passing the values directly would be a simpler and more maintainable solution.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I agree that direct pass-through would create a thinner binding, but cedarling_wasm already provides that low-level API.

The goal of @janssenproject/cedarling is a stable, Web-native SDK. Its typed CedarlingOptions also supports JavaScript-specific sources such as Uint8Array archives and async loaders, which require routing through init_from_archive_bytes.

Passing CEDARLING_* values directly would expose the generated/core contract to every consumer and duplicate the existing WASM API. The official bootstrap documentation should remain the source of truth for Cedarling semantics, while this SDK maintains a curated, versioned JS interface with compatibility tests.

I therefore propose keeping the typed mapping. Consumers needing raw bootstrap access can use cedarling_wasm, or we can discuss a separate explicitly low-level API.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, maybe at least we should have a method to build a config based on raw bootstrap properties; maybe for someone it will be useful.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, thank . I think this is feasible.

@Dahkenangnon

Copy link
Copy Markdown
Author

Blocking

Signature validation is untested, and the shipped example disables it

Every contract test that runs real WASM authorization disables it:

The rationale (deterministic offline runs) is already acknowledged in the thread. The gap that remains: if a change stopped verifying signatures, no test in this suite would fail.

The demo IdP at common/idp.js:115 already generates an RSA keypair and serves JWKS, so a valid-signature case and a tampered-signature case can both run offline.

The same defect appears at runtime: common/test-config.json:10-11 sets dangerouslyDisableSignatureValidation and dangerouslyDisableStatusValidation for every example, so the demos' "signed" path accepts forged tokens. Tests don't verify, and the shipped example turns verification off — those should be fixed together.

The publish job cannot authenticate

build_cedarling_js runs npm publish at build-packages.yml:750 with no NODE_AUTH_TOKEN, no .npmrc, and no registry-url: on setup-node at :622. The sibling build_cedarling_wasm job sets it at :445.

The job therefore depends entirely on npm OIDC trusted publishing, hence npm@11.9.0 at :629. Trusted publishing must be configured against a package that already exists on npm; @janssenproject/cedarling does not. The first release will fail until someone publishes manually and configures the trusted publisher for this exact workflow and job name.

The PR description states the SDK is published "using the existing npm publishing secret." No secret is referenced in the job — either the description or the implementation needs correcting.

npm ci runs against a swapped-in file: dependency

build-packages.yml:638 runs npm version "${WASM_VERSION}" after the downloaded artifact has replaced cedarling_wasm/pkg, then :687 runs npm ci against the committed lockfile. dependencies pins file:../cedarling_wasm/pkg, and npm validates linked-package versions against the lock, so a version-bumped artifact is likely to produce "package.json and package-lock.json are not in sync."

This passes in test-cedarling.yml only because wasm-pack there builds the unbumped Cargo.toml version. The workflow_dispatch dry run noted under Release path would confirm or rule this out.

Three call sites reject where their siblings do not

These re-throw when the caught error is not a recognized SDK code:

The issuer, context, and log helpers handle the same case by wrapping. See client.ts:302-315, which uses isSdkErrorCode(...) ? error : createSdkError(...).

The doc comment indicates the throw is intentional for unexpected failures. The issue is the asymmetry: authorize, close, and createCedarling can reject while structurally identical methods never do, and the API shape leads callers to check result.ok and stop there. That produces an unhandled rejection in production.

README.md:366 states the SDK "does not throw errors on expected operational failures". Accurate as written, but "expected" is load-bearing and easy to miss. Either wrap consistently or document the rejection path.

authz-middleware.js — unguarded dereference and an ownership fallback

authz-middleware.js:88 reads diagnostics.reasons unguarded, while :72 correctly guards the same value with if (!decision && diagnostics). A deny without diagnostics throws inside the error path.

:13 sets taskOwner = task ? task.owner : userId, so a missing resource defaults ownership to the caller and any owner-based policy allows. With x-user-id defaulting to 'bob' at :10, and signature validation off per the section above, this is the file consumers are most likely to copy. It needs an explicit "intentionally insecure, do not copy" banner at minimum.

Non-blocking

The guard at assert-publishable.mjs:28 checks name === "cedarling_wasm", but the dependency is @janssenproject/cedarling_wasm, so the branch never executes. Returning false would also mark it unsafe, which inverts the apparent intent. Staging rewrites the version first, so nothing breaks today.

With allowedAlgorithms omitted, all twelve algorithms are enabled: prepare.ts:24-36, applied at prepare.ts:503. none is absent, so this is not urgent. Defaulting to symmetric HS* alongside RS/ES/PS is the standard setup for algorithm confusion. Asymmetric by default, with HS* opt-in, would be safer.

prepare.ts:21 defines UINT32_MAX as "the largest integer representable by the generated unsigned 32-bit fields", but five fields bound to Number.MAX_SAFE_INTEGER instead — CEDARLING_DATA_STORE_MAX_TTL and _DEFAULT_TTL at :437-451, CEDARLING_HTTP_MAX_RESPONSE_SIZE_BYTES at :618, and the three CEDARLING_LOCK_*_INTERVAL values. Validation accepts values the WASM boundary will reject or wrap.

httpUrl() at prepare.ts:177 rejects credentials in the URL but accepts plaintext http:. It gates policyStore.url and lock.configurationUrl, the latter alongside CEDARLING_LOCK_SSA_JWT. Requiring https: outside loopback would match the algorithm-default reasoning above.

withAuthorizationShortcuts at client.ts:80 sets denied: false on the error branch, so if (result.denied) reject() fails open while if (!result.allowed) is safe. Since the shortcuts exist to encourage terse call sites, that asymmetry belongs in the type's doc comment.

selectAuthorizationRequest in authorization/dispatch.ts reads type and request and silently ignores unknown envelope fields, while every other boundary in the SDK calls rejectUnknown.

SDK and WASM versions are permanently coupled: build-packages.yml:680 runs npm version "${WASM_VERSION}" and stage-release.mjs:110 pins exactly. A WASM patch therefore forces an SDK republish, and the published cedarling_wasm version stops matching the Rust crate. Confirming this is the intended long-term behaviour would be useful.

query.ts:94 is unreachable. Given the present === 0 check above it and the id !== undefined early return, requestId and tag cannot both be undefined at that point.

stage-release.mjs:120 spreads ...sourceManifest, so all 10 devDependencies (electron, playwright, webpack, workerd) and 24 test:* scripts land in the published package.json. Consumers do not install devDeps, so the effect is cosmetic.

test-config.json declares expectedFailures: ["authorizeMultiIssuer"] described as a "known bug", echoed by a console.log in the demo's init.js. A linked tracking issue would be better than prose in a fixture.

The new cedarling_js_tests job builds the WASM crate and installs Bun, Deno, and three Playwright browsers to run eight runtime suites, with no timeout-minutes and no persist-credentials: false on checkout. The latter is set on the new build-packages.yml job.

Release path

build-packages.yml triggers on workflow_run and workflow_dispatch only, never on pull_request. The 257 lines of publish logic added here (version coordination, manifest staging, tarball verification, npm publish) have not executed. The green checks on this PR come from test-cedarling.yml. A workflow_dispatch dry run against nightly would validate it — and would settle the npm ci and npm-authentication questions above — before anything depends on it.

Note that the new SDK publish at build-packages.yml:750 fails loudly, whereas the existing WASM publish at line 501 continues on error. Since build_cedarling_js depends on that job and then polls npm view for five minutes, a silently failed WASM publish surfaces here as a confusing timeout.

Minor: the new collect-cedarling-js-digests tag step at :1977 interpolates inputs.target_tag and workflow_run.head_branch directly into run:. This matches the existing pattern used by the other collect-* jobs in the file, so it is not a regression, but build_cedarling_js itself uses the safer env: form and the new job could match it.

CodeRabbit skipped this PR ("249 files exceed the limit of 150"). With 29,181 lines of committed lockfiles and an Electron demo carrying its own webpack configs, the PR exceeds the bots' limits. Splitting the SDK from the demos would restore that coverage.

Checked, no findings

Publish security — injection and secret handling: actions are SHA-pinned, harden-runner present, persist-credentials: false, github.repository fork guard, --provenance with OIDC, SLSA provenance job, WASM package name and version asserted before publish. No injection or token-leak paths found. (Authentication is covered separately under Blocking.)

No committed secrets. The demo IdP generates its keypair at runtime with generateKeyPairSync. The Electron demo sets contextIsolation: true, sandbox: true, nodeIntegration: false, and routes external URLs through shell.openExternal.

Error model: errors.ts:229 produces frozen Error objects, strips URL credentials, and gates retained causes through a WeakSet allowlist. One trade-off: every message becomes a fixed generic string, so the underlying Cedar and WASM detail is discarded. This may make production debugging harder than expected; a debug flag preserving detail would address it.

Input validation is the strongest part of the SDK: inspectOwnProperty avoids invoking getters, rejectUnknown catches misspelled options deterministically, and values are snapshotted before crossing the WASM boundary. Lifecycle handling — in-flight accounting, a single memoized close promise, drain-before-shutdown — is careful. scripts/verify-consumer.mjs packs, installs offline into a scratch consumer, asserts the WASM dependency hoists rather than nests, and exercises both ESM and CJS entry points.

Archive bytes are copied during validation at prepare.ts:302, so the factory does not need to copy again. The SDK lockfile is committed so npm ci resolves in CI. The Cargo workspace exclusion is harmless, though bindings/cedarling_js contains no Cargo.toml, so the entry is a no-op.

Ack and working on these in child PR.

Comment on lines +63 to +91
/**
* Dispatches an unsigned authorization envelope to `authorizeUnsigned()`.
*
* @param request - Explicit unsigned authorization envelope.
* @returns The named operation's decision or an error labeled `authorize`.
*/
authorize(
request: UnsignedAuthorization,
): Promise<AuthorizationResult<CedarlingAuthorizationError>>;

/**
* Dispatches a multi-issuer envelope to `authorizeMultiIssuer()`.
*
* @param request - Explicit token-validating authorization envelope.
* @returns The named operation's decision or an error labeled `authorize`.
*/
authorize(
request: MultiIssuerAuthorization,
): Promise<AuthorizationResult<CedarlingAuthorizationError>>;

/**
* Dispatches a discriminated authorization union to one named operation.
*
* @param request - Unsigned or multi-issuer authorization envelope.
* @returns The selected named operation's decision with dispatcher errors.
*/
authorize(
request: AuthorizationRequest,
): Promise<AuthorizationResult<CedarlingAuthorizationError>>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of having authorizeUnsigned and authorizeMultiIssuer we have 3 other method that do the same, do we really need that? KISS & YAGNI

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The three declarations are TypeScript overload signatures for one runtime dispatcher, but I agree that the dispatcher itself duplicates the two named authorization operations. Go and Python expose only unsigned and multi-issuer methods, and those names make the selected trust model explicit. I will remove the generic authorize() API and retain authorizeUnsigned() and authorizeMultiIssuer().

* if (!closed.ok) console.error(closed.error.code);
* ```
*/
close(): Promise<Result<void, CedarlingLifecycleError>>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In WASM we have method shut_down I think better to have the same naming. To be consistent over different bindings.

@olehbozhok olehbozhok Jul 29, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method waits until all workers are finished

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the explicit intention to have less difference on public api across bindings (python, go, js,etc) goal, renaming close() to shutDown() is reasonable and still idiomatic TypeScript.

So, I will rename it to shutDown() .


```ts
const result = await client.authorizeUnsigned(request);
const { ok, allowed, denied, err } = result;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

allowed and denied is the same bool value

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed! I'll remove these flat

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>
Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>
Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>
Dahkenangnon and others added 10 commits August 6, 2026 18:05
Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>
Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>
Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>
Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>
Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>
…mples

Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>
…ples

Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>
Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>
* feat(cedarling-js)!: align public API with Cedarling bindings

Accept typed options or raw bootstrap properties through the single factory.

Expose only explicit unsigned and multi-issuer authorization methods, return canonical authorization results, and rename close to shutDown.

Update documentation, consumer verification, and contract coverage for the revised API.

Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>

* test(cedarling-js): strengthen SDK contract coverage

Cover raw bootstrap capabilities, retained-log expiry and drain lifecycle, and context clearing behavior.

Expand signature-enabled multi-issuer validation for expired, untrusted, tampered, and disallowed-algorithm tokens.

Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>

* refactor(cedarling-js): consolidate SDK internals

Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>

* feat(cedarling-js): harden validation and diagnostics

Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>

* ci(cedarling-js): harden test and release workflows

Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>

* docs(cedarling-js): address remaining review feedback

Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>

* ci(cedarling-js): harden npm bootstrap and fix doc lint warnings

- Pin npm@11.9.0 tarball with SHA-512 integrity check in build-packages.yml

- Disable lifecycle scripts during npm bootstrap

- Remove redundant "same exact" wording in docs/README.md

- Silence markdownlint MD028 false positives for GitHub alert blockquotes

Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>

* feat(cedarling-js): support entity references and harden value validation

- Add CedarEntityReference type and include it in CedarValue union

- Validate { __entity: { type, id } } markers in snapshotCedarValueInner

- Reject reserved cedar_entity_mapping key anywhere in Cedar values

- Return required (not type) error when action field is missing

- Assert shutDown() Result in verify-consumer.mjs ESM/CJS simulations

Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>

* test(cedarling-js): cover entity references, reserved keys, and missing action

- Add entity reference validation tests in common-values.test.ts

- Add cedar_entity_mapping rejection tests for nested and array cases

- Add missing action required-error test in authorize-unsigned.test.ts

Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>

---------

Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>
* feat(cedarling-js): harden runtime examples

Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>

* fix(cedarling-js): apply review feedback to runtime examples

- unquote the single-word Lato font fallback in the shared theme
- split MAIN_ARGS into individual arguments for the Electron dev server
- bound issuer configuration fetches with abort timeouts
- reject empty RS256 algorithm allowlists in Cedarling config loading
- map signed identity failures to 401 and operational failures to 503
- filter tasks:list to outcomes the user is authorized to view
- centralize Cedar identifiers and actions in shared contracts
- reset permission state, trim titles, and catch logout errors in renderers
- time-box Cedarling shutdown and drain pending IPC work on quit
- bind the stored ID-token subject to the UserInfo token
- derive cookie Secure from APP_ORIGIN behind TLS-terminating proxies
- authenticate before task existence checks and announce permission checks
- add tests for denied/error authorization outcomes and mutation happy paths

Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>

* fix(cedarling-js): address review feedback on authorization boundary

- Refactor authz-middleware.js to verify JWT via jose and derive userId
  from the verified token subject instead of the spoofable x-user-id
  header (concern from olehbozhok)
- Extract checkSessionGuard as a pure exported function in ipc.ts for
  independent testability
- Add owner-mismatch 403 test to server.test.js (alice deletes bob's task)
- Add invalid signed token rejection test to server.test.js
- Add policy-deny test to permissions.test.ts (decision: false)
- Add signed-mode happy path test to permissions.test.ts
- Add unit tests for checkSessionGuard in ipc.test.ts (no session,
  session match, session mismatch)

Addresses olehbozhok's review comment on PR #14677.

Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>

* fix(cedarling-js): close remaining review findings on shell usage and RS256 allowlist

- Spawn npm without a shell on POSIX in webpack.config.renderer.dev.ts;
  keep shell only on Windows where the npm.cmd shim requires it
- Reject an empty jwt.allowedAlgorithms array in the vercel-nextjs
  Cedarling config guard, matching the hono example

Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>

* feat(cedarling-js): support pre-staged local package installs

Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>

* feat(cedarling-js): add reproducible Docker build targets

Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>

* feat(cedarling-js): orchestrate examples with Compose profiles

Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>

* fix(cedarling-js): unify Cloudflare Hono workflow

Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>

* refactor(cedarling-js): migrate Electron demo to electron-vite

Replace the ERB and Webpack scaffold with the documented electron-vite layout while retaining the Docker-assisted local package build, external IdP, native GUI workflow, and Cedarling security boundary.

Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>

* fix(cedarling-js): add missing init:true to hono-deno service

hono-deno was the only compose service without init:true, unlike
its sibling stacks. Without it, deno runs as PID 1 and must handle
SIGTERM/zombie-reaping itself, causing slower/unclean shutdown.

Signed-off-by: Oleh Bozhok <6554798+olehbozhok@users.noreply.github.com>

---------

Signed-off-by: Justin Dah-kenangnon <dah.kenangnon@gmail.com>
Signed-off-by: Oleh Bozhok <6554798+olehbozhok@users.noreply.github.com>
Co-authored-by: Oleh Bozhok <6554798+olehbozhok@users.noreply.github.com>
@Dahkenangnon
Dahkenangnon force-pushed the jans-cedarling-14582 branch from 355336a to b392987 Compare August 6, 2026 17:07
)

* refactor: replace assert error in favor runtime exception

Signed-off-by: iromli <isman.firmansyah@gmail.com>

* refactor: change RuntimeError with specific CalledProcessError

Signed-off-by: iromli <isman.firmansyah@gmail.com>

* chore: sync templates

Signed-off-by: iromli <isman.firmansyah@gmail.com>

* chore: revert to RuntimeError

Signed-off-by: iromli <isman.firmansyah@gmail.com>

---------

Signed-off-by: iromli <isman.firmansyah@gmail.com>
Co-authored-by: Mohammad Abudayyeh <47318409+moabu@users.noreply.github.com>
@Dahkenangnon
Dahkenangnon requested a review from iromli as a code owner August 7, 2026 10:32
@Dahkenangnon
Dahkenangnon temporarily deployed to integration-tests August 7, 2026 10:39 — with GitHub Actions Inactive
@Dahkenangnon
Dahkenangnon temporarily deployed to integration-tests August 7, 2026 10:39 — with GitHub Actions Inactive
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-documentation Documentation needs to change as part of issue or PR comp-docs Touching folder /docs comp-jans-cedarling Touching folder /jans-cedarling kind-feature Issue or PR is a new feature request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(cedarling): add a stable JavaScript SDK over the WASM bindings

7 participants