@nimbus-sh/worker 0.1.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 +219 -0
- package/dist/_shared/bytes.d.ts +18 -0
- package/dist/_shared/bytes.d.ts.map +1 -0
- package/dist/_shared/bytes.js +17 -0
- package/dist/_shared/cache-stats.d.ts +146 -0
- package/dist/_shared/cache-stats.d.ts.map +1 -0
- package/dist/_shared/cache-stats.js +162 -0
- package/dist/_shared/exports-resolver.d.ts +76 -0
- package/dist/_shared/exports-resolver.d.ts.map +1 -0
- package/dist/_shared/exports-resolver.js +284 -0
- package/dist/_shared/install-phase.d.ts +47 -0
- package/dist/_shared/install-phase.d.ts.map +1 -0
- package/dist/_shared/install-phase.js +1 -0
- package/dist/_shared/real-node-imports.d.ts +38 -0
- package/dist/_shared/real-node-imports.d.ts.map +1 -0
- package/dist/_shared/real-node-imports.js +46 -0
- package/dist/_shared/retry.d.ts +93 -0
- package/dist/_shared/retry.d.ts.map +1 -0
- package/dist/_shared/retry.js +161 -0
- package/dist/_shared/session-id.d.ts +39 -0
- package/dist/_shared/session-id.d.ts.map +1 -0
- package/dist/_shared/session-id.js +127 -0
- package/dist/_shared/session-router.d.ts +104 -0
- package/dist/_shared/session-router.d.ts.map +1 -0
- package/dist/_shared/session-router.js +165 -0
- package/dist/_shared/w7-frame.d.ts +93 -0
- package/dist/_shared/w7-frame.d.ts.map +1 -0
- package/dist/_shared/w7-frame.js +402 -0
- package/dist/auth/index.d.ts +16 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +15 -0
- package/dist/auth/middleware.d.ts +85 -0
- package/dist/auth/middleware.d.ts.map +1 -0
- package/dist/auth/middleware.js +150 -0
- package/dist/auth/token.d.ts +96 -0
- package/dist/auth/token.d.ts.map +1 -0
- package/dist/auth/token.js +226 -0
- package/dist/auth/types.d.ts +141 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +114 -0
- package/dist/bindings/d1.d.ts +102 -0
- package/dist/bindings/d1.d.ts.map +1 -0
- package/dist/bindings/d1.js +549 -0
- package/dist/bindings/kv.d.ts +82 -0
- package/dist/bindings/kv.d.ts.map +1 -0
- package/dist/bindings/kv.js +290 -0
- package/dist/bindings/r2.d.ts +137 -0
- package/dist/bindings/r2.d.ts.map +1 -0
- package/dist/bindings/r2.js +379 -0
- package/dist/cirrus-npm-cjs.generated.d.ts +17 -0
- package/dist/cirrus-npm-cjs.generated.d.ts.map +1 -0
- package/dist/cirrus-npm-cjs.generated.js +41 -0
- package/dist/cirrus-plugin-react.generated.d.ts +34 -0
- package/dist/cirrus-plugin-react.generated.d.ts.map +1 -0
- package/dist/cirrus-plugin-react.generated.js +33 -0
- package/dist/constants.d.ts +34 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +116 -0
- package/dist/esbuild-wasm-bundle.generated.d.ts +41 -0
- package/dist/esbuild-wasm-bundle.generated.d.ts.map +1 -0
- package/dist/esbuild-wasm-bundle.generated.js +40 -0
- package/dist/facets/cirrus-real.d.ts +147 -0
- package/dist/facets/cirrus-real.d.ts.map +1 -0
- package/dist/facets/cirrus-real.js +981 -0
- package/dist/facets/inner-do-registry.d.ts +41 -0
- package/dist/facets/inner-do-registry.d.ts.map +1 -0
- package/dist/facets/inner-do-registry.js +51 -0
- package/dist/facets/manager.d.ts +314 -0
- package/dist/facets/manager.d.ts.map +1 -0
- package/dist/facets/manager.js +2681 -0
- package/dist/facets/process.d.ts +318 -0
- package/dist/facets/process.d.ts.map +1 -0
- package/dist/facets/process.js +567 -0
- package/dist/facets/real-vite-fs-shim.d.ts +151 -0
- package/dist/facets/real-vite-fs-shim.d.ts.map +1 -0
- package/dist/facets/real-vite-fs-shim.js +1279 -0
- package/dist/facets/real-vite-hmr.d.ts +142 -0
- package/dist/facets/real-vite-hmr.d.ts.map +1 -0
- package/dist/facets/real-vite-hmr.js +476 -0
- package/dist/facets/vite-dev-server.d.ts +293 -0
- package/dist/facets/vite-dev-server.d.ts.map +1 -0
- package/dist/facets/vite-dev-server.js +2580 -0
- package/dist/facets/wasm-swap-registry.d.ts +261 -0
- package/dist/facets/wasm-swap-registry.d.ts.map +1 -0
- package/dist/facets/wasm-swap-registry.js +551 -0
- package/dist/facets/ws-terminal.d.ts +107 -0
- package/dist/facets/ws-terminal.d.ts.map +1 -0
- package/dist/facets/ws-terminal.js +207 -0
- package/dist/git/commands.d.ts +13 -0
- package/dist/git/commands.d.ts.map +1 -0
- package/dist/git/commands.js +633 -0
- package/dist/git/network-facet.d.ts +61 -0
- package/dist/git/network-facet.d.ts.map +1 -0
- package/dist/git/network-facet.js +700 -0
- package/dist/git-bundle.generated.d.ts +16 -0
- package/dist/git-bundle.generated.d.ts.map +1 -0
- package/dist/git-bundle.generated.js +15 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +73 -0
- package/dist/loaders/child-process/spawn-facet.d.ts +90 -0
- package/dist/loaders/child-process/spawn-facet.d.ts.map +1 -0
- package/dist/loaders/child-process/spawn-facet.js +103 -0
- package/dist/loaders/child-process/spawn-pool.d.ts +110 -0
- package/dist/loaders/child-process/spawn-pool.d.ts.map +1 -0
- package/dist/loaders/child-process/spawn-pool.js +161 -0
- package/dist/loaders/fanout-pool.d.ts +195 -0
- package/dist/loaders/fanout-pool.d.ts.map +1 -0
- package/dist/loaders/fanout-pool.js +326 -0
- package/dist/loaders/generated-workers.d.ts +26 -0
- package/dist/loaders/generated-workers.d.ts.map +1 -0
- package/dist/loaders/generated-workers.js +25 -0
- package/dist/loaders/index.d.ts +15 -0
- package/dist/loaders/index.d.ts.map +1 -0
- package/dist/loaders/index.js +13 -0
- package/dist/loaders/loader-pool.d.ts +273 -0
- package/dist/loaders/loader-pool.d.ts.map +1 -0
- package/dist/loaders/loader-pool.js +624 -0
- package/dist/loaders/npm-resolve-preamble.d.ts +34 -0
- package/dist/loaders/npm-resolve-preamble.d.ts.map +1 -0
- package/dist/loaders/npm-resolve-preamble.js +237 -0
- package/dist/loaders/pre-bundle-preamble.d.ts +41 -0
- package/dist/loaders/pre-bundle-preamble.d.ts.map +1 -0
- package/dist/loaders/pre-bundle-preamble.js +114 -0
- package/dist/loaders/vendor/codegen.d.ts +24 -0
- package/dist/loaders/vendor/codegen.d.ts.map +1 -0
- package/dist/loaders/vendor/codegen.js +44 -0
- package/dist/loaders/vendor/errors.d.ts +24 -0
- package/dist/loaders/vendor/errors.d.ts.map +1 -0
- package/dist/loaders/vendor/errors.js +46 -0
- package/dist/loaders/vendor/index.d.ts +16 -0
- package/dist/loaders/vendor/index.d.ts.map +1 -0
- package/dist/loaders/vendor/index.js +11 -0
- package/dist/loaders/vendor/pool.d.ts +88 -0
- package/dist/loaders/vendor/pool.d.ts.map +1 -0
- package/dist/loaders/vendor/pool.js +353 -0
- package/dist/loaders/vendor/primitives.d.ts +11 -0
- package/dist/loaders/vendor/primitives.d.ts.map +1 -0
- package/dist/loaders/vendor/primitives.js +33 -0
- package/dist/loaders/vendor/serialize.d.ts +3 -0
- package/dist/loaders/vendor/serialize.d.ts.map +1 -0
- package/dist/loaders/vendor/serialize.js +25 -0
- package/dist/loaders/vendor/types.d.ts +60 -0
- package/dist/loaders/vendor/types.d.ts.map +1 -0
- package/dist/loaders/vendor/types.js +4 -0
- package/dist/npm/cache.d.ts +136 -0
- package/dist/npm/cache.d.ts.map +1 -0
- package/dist/npm/cache.js +429 -0
- package/dist/npm/install-batch-facet.d.ts +129 -0
- package/dist/npm/install-batch-facet.d.ts.map +1 -0
- package/dist/npm/install-batch-facet.js +598 -0
- package/dist/npm/install-facet.d.ts +29 -0
- package/dist/npm/install-facet.d.ts.map +1 -0
- package/dist/npm/install-facet.js +9 -0
- package/dist/npm/installer.d.ts +239 -0
- package/dist/npm/installer.d.ts.map +1 -0
- package/dist/npm/installer.js +2022 -0
- package/dist/npm/npx-install.d.ts +74 -0
- package/dist/npm/npx-install.d.ts.map +1 -0
- package/dist/npm/npx-install.js +302 -0
- package/dist/npm/pre-bundle-facet.d.ts +168 -0
- package/dist/npm/pre-bundle-facet.d.ts.map +1 -0
- package/dist/npm/pre-bundle-facet.js +653 -0
- package/dist/npm/r2-cache.d.ts +286 -0
- package/dist/npm/r2-cache.d.ts.map +1 -0
- package/dist/npm/r2-cache.js +506 -0
- package/dist/npm/resolve-facet.d.ts +202 -0
- package/dist/npm/resolve-facet.d.ts.map +1 -0
- package/dist/npm/resolve-facet.js +833 -0
- package/dist/npm/resolve-one-facet.d.ts +204 -0
- package/dist/npm/resolve-one-facet.d.ts.map +1 -0
- package/dist/npm/resolve-one-facet.js +437 -0
- package/dist/npm/resolver.d.ts +109 -0
- package/dist/npm/resolver.d.ts.map +1 -0
- package/dist/npm/resolver.js +899 -0
- package/dist/npm/tarball-stream.d.ts +85 -0
- package/dist/npm/tarball-stream.d.ts.map +1 -0
- package/dist/npm/tarball-stream.js +205 -0
- package/dist/npm/tarball.d.ts +51 -0
- package/dist/npm/tarball.d.ts.map +1 -0
- package/dist/npm/tarball.js +185 -0
- package/dist/observability/diag-counters.d.ts +292 -0
- package/dist/observability/diag-counters.d.ts.map +1 -0
- package/dist/observability/diag-counters.js +302 -0
- package/dist/observability/heap-estimate.d.ts +166 -0
- package/dist/observability/heap-estimate.d.ts.map +1 -0
- package/dist/observability/heap-estimate.js +192 -0
- package/dist/observability/heavy-alloc-coord.d.ts +95 -0
- package/dist/observability/heavy-alloc-coord.d.ts.map +1 -0
- package/dist/observability/heavy-alloc-coord.js +149 -0
- package/dist/observability/oom-classify.d.ts +56 -0
- package/dist/observability/oom-classify.d.ts.map +1 -0
- package/dist/observability/oom-classify.js +118 -0
- package/dist/observability/oom-discriminator.d.ts +192 -0
- package/dist/observability/oom-discriminator.d.ts.map +1 -0
- package/dist/observability/oom-discriminator.js +233 -0
- package/dist/real-vite-bundle.generated.d.ts +46 -0
- package/dist/real-vite-bundle.generated.d.ts.map +1 -0
- package/dist/real-vite-bundle.generated.js +53 -0
- package/dist/replica/routing.d.ts +171 -0
- package/dist/replica/routing.d.ts.map +1 -0
- package/dist/replica/routing.js +288 -0
- package/dist/replica/suspension.d.ts +36 -0
- package/dist/replica/suspension.d.ts.map +1 -0
- package/dist/replica/suspension.js +49 -0
- package/dist/router/index.d.ts +156 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/router/index.js +220 -0
- package/dist/runtime/assets-loader.d.ts +71 -0
- package/dist/runtime/assets-loader.d.ts.map +1 -0
- package/dist/runtime/assets-loader.js +128 -0
- package/dist/runtime/barrel-detect.d.ts +46 -0
- package/dist/runtime/barrel-detect.d.ts.map +1 -0
- package/dist/runtime/barrel-detect.js +73 -0
- package/dist/runtime/barrel-synthesizer.d.ts +181 -0
- package/dist/runtime/barrel-synthesizer.d.ts.map +1 -0
- package/dist/runtime/barrel-synthesizer.js +541 -0
- package/dist/runtime/bun-repl.d.ts +46 -0
- package/dist/runtime/bun-repl.d.ts.map +1 -0
- package/dist/runtime/bun-repl.js +326 -0
- package/dist/runtime/bun-runner.d.ts +66 -0
- package/dist/runtime/bun-runner.d.ts.map +1 -0
- package/dist/runtime/bun-runner.js +219 -0
- package/dist/runtime/clang-runner.d.ts +38 -0
- package/dist/runtime/clang-runner.d.ts.map +1 -0
- package/dist/runtime/clang-runner.js +1033 -0
- package/dist/runtime/comment-strip.d.ts +37 -0
- package/dist/runtime/comment-strip.d.ts.map +1 -0
- package/dist/runtime/comment-strip.js +74 -0
- package/dist/runtime/esbuild-service.d.ts +201 -0
- package/dist/runtime/esbuild-service.d.ts.map +1 -0
- package/dist/runtime/esbuild-service.js +1444 -0
- package/dist/runtime/esbuild-wasm-bytes.d.ts +87 -0
- package/dist/runtime/esbuild-wasm-bytes.d.ts.map +1 -0
- package/dist/runtime/esbuild-wasm-bytes.js +131 -0
- package/dist/runtime/framework-detect.d.ts +71 -0
- package/dist/runtime/framework-detect.d.ts.map +1 -0
- package/dist/runtime/framework-detect.js +178 -0
- package/dist/runtime/long-running-handle.d.ts +133 -0
- package/dist/runtime/long-running-handle.d.ts.map +1 -0
- package/dist/runtime/long-running-handle.js +192 -0
- package/dist/runtime/node-repl.d.ts +36 -0
- package/dist/runtime/node-repl.d.ts.map +1 -0
- package/dist/runtime/node-repl.js +293 -0
- package/dist/runtime/node-runner.d.ts +111 -0
- package/dist/runtime/node-runner.d.ts.map +1 -0
- package/dist/runtime/node-runner.js +194 -0
- package/dist/runtime/node-shims.d.ts +24 -0
- package/dist/runtime/node-shims.d.ts.map +1 -0
- package/dist/runtime/node-shims.js +3019 -0
- package/dist/runtime/package-manager.d.ts +118 -0
- package/dist/runtime/package-manager.d.ts.map +1 -0
- package/dist/runtime/package-manager.js +420 -0
- package/dist/runtime/port-registry.d.ts +80 -0
- package/dist/runtime/port-registry.d.ts.map +1 -0
- package/dist/runtime/port-registry.js +166 -0
- package/dist/runtime/process-logs-api.d.ts +102 -0
- package/dist/runtime/process-logs-api.d.ts.map +1 -0
- package/dist/runtime/process-logs-api.js +253 -0
- package/dist/runtime/process-logs.d.ts +311 -0
- package/dist/runtime/process-logs.d.ts.map +1 -0
- package/dist/runtime/process-logs.js +656 -0
- package/dist/runtime/process-table.d.ts +65 -0
- package/dist/runtime/process-table.d.ts.map +1 -0
- package/dist/runtime/process-table.js +107 -0
- package/dist/runtime/project-detect.d.ts +21 -0
- package/dist/runtime/project-detect.d.ts.map +1 -0
- package/dist/runtime/project-detect.js +46 -0
- package/dist/runtime/python-repl.d.ts +66 -0
- package/dist/runtime/python-repl.d.ts.map +1 -0
- package/dist/runtime/python-repl.js +563 -0
- package/dist/runtime/python-runner.d.ts +50 -0
- package/dist/runtime/python-runner.d.ts.map +1 -0
- package/dist/runtime/python-runner.js +870 -0
- package/dist/runtime/repl-session.d.ts +160 -0
- package/dist/runtime/repl-session.d.ts.map +1 -0
- package/dist/runtime/repl-session.js +393 -0
- package/dist/runtime/require-resolver.d.ts +52 -0
- package/dist/runtime/require-resolver.d.ts.map +1 -0
- package/dist/runtime/require-resolver.js +690 -0
- package/dist/runtime/router-basename.d.ts +36 -0
- package/dist/runtime/router-basename.d.ts.map +1 -0
- package/dist/runtime/router-basename.js +310 -0
- package/dist/runtime/ruby-repl.d.ts +43 -0
- package/dist/runtime/ruby-repl.d.ts.map +1 -0
- package/dist/runtime/ruby-repl.js +229 -0
- package/dist/runtime/ruby-runner.d.ts +62 -0
- package/dist/runtime/ruby-runner.d.ts.map +1 -0
- package/dist/runtime/ruby-runner.js +737 -0
- package/dist/runtime/runtime-catalog.d.ts +99 -0
- package/dist/runtime/runtime-catalog.d.ts.map +1 -0
- package/dist/runtime/runtime-catalog.js +183 -0
- package/dist/runtime/runtime-registry.d.ts +136 -0
- package/dist/runtime/runtime-registry.d.ts.map +1 -0
- package/dist/runtime/runtime-registry.js +318 -0
- package/dist/runtime/streams.d.ts +13 -0
- package/dist/runtime/streams.d.ts.map +1 -0
- package/dist/runtime/streams.js +402 -0
- package/dist/runtime/wasi-instance.d.ts +190 -0
- package/dist/runtime/wasi-instance.d.ts.map +1 -0
- package/dist/runtime/wasi-instance.js +2044 -0
- package/dist/runtime/wasm-runner.d.ts +117 -0
- package/dist/runtime/wasm-runner.d.ts.map +1 -0
- package/dist/runtime/wasm-runner.js +705 -0
- package/dist/session/bindings.d.ts +168 -0
- package/dist/session/bindings.d.ts.map +1 -0
- package/dist/session/bindings.js +524 -0
- package/dist/session/ctx-exports.d.ts +16 -0
- package/dist/session/ctx-exports.d.ts.map +1 -0
- package/dist/session/ctx-exports.js +22 -0
- package/dist/session/diag.d.ts +62 -0
- package/dist/session/diag.d.ts.map +1 -0
- package/dist/session/diag.js +118 -0
- package/dist/session/fs-watch.d.ts +132 -0
- package/dist/session/fs-watch.d.ts.map +1 -0
- package/dist/session/fs-watch.js +255 -0
- package/dist/session/helpers.d.ts +150 -0
- package/dist/session/helpers.d.ts.map +1 -0
- package/dist/session/helpers.js +472 -0
- package/dist/session/hibernation.d.ts +145 -0
- package/dist/session/hibernation.d.ts.map +1 -0
- package/dist/session/hibernation.js +385 -0
- package/dist/session/init-phases.d.ts +87 -0
- package/dist/session/init-phases.d.ts.map +1 -0
- package/dist/session/init-phases.js +137 -0
- package/dist/session/init.d.ts +46 -0
- package/dist/session/init.d.ts.map +1 -0
- package/dist/session/init.js +3579 -0
- package/dist/session/keys.d.ts +56 -0
- package/dist/session/keys.d.ts.map +1 -0
- package/dist/session/keys.js +55 -0
- package/dist/session/nimbus-session.d.ts +434 -0
- package/dist/session/nimbus-session.d.ts.map +1 -0
- package/dist/session/nimbus-session.js +1084 -0
- package/dist/session/programmatic.d.ts +105 -0
- package/dist/session/programmatic.d.ts.map +1 -0
- package/dist/session/programmatic.js +326 -0
- package/dist/session/replica-routes.d.ts +68 -0
- package/dist/session/replica-routes.d.ts.map +1 -0
- package/dist/session/replica-routes.js +77 -0
- package/dist/session/routes.d.ts +29 -0
- package/dist/session/routes.d.ts.map +1 -0
- package/dist/session/routes.js +1331 -0
- package/dist/session/rpc.d.ts +323 -0
- package/dist/session/rpc.d.ts.map +1 -0
- package/dist/session/rpc.js +808 -0
- package/dist/session/state-store.d.ts +210 -0
- package/dist/session/state-store.d.ts.map +1 -0
- package/dist/session/state-store.js +459 -0
- package/dist/session/supervisor-rpc.d.ts +269 -0
- package/dist/session/supervisor-rpc.d.ts.map +1 -0
- package/dist/session/supervisor-rpc.js +437 -0
- package/dist/session/ws-hibernation-config.d.ts +64 -0
- package/dist/session/ws-hibernation-config.d.ts.map +1 -0
- package/dist/session/ws-hibernation-config.js +89 -0
- package/dist/session/ws.d.ts +95 -0
- package/dist/session/ws.d.ts.map +1 -0
- package/dist/session/ws.js +395 -0
- package/dist/shell/features.d.ts +410 -0
- package/dist/shell/features.d.ts.map +1 -0
- package/dist/shell/features.js +1885 -0
- package/dist/shell/unix-commands.d.ts +15 -0
- package/dist/shell/unix-commands.d.ts.map +1 -0
- package/dist/shell/unix-commands.js +3703 -0
- package/dist/stubs/lifo-ui.d.ts +10 -0
- package/dist/stubs/lifo-ui.d.ts.map +1 -0
- package/dist/stubs/lifo-ui.js +1 -0
- package/dist/tailwind-play.generated.d.ts +19 -0
- package/dist/tailwind-play.generated.d.ts.map +1 -0
- package/dist/tailwind-play.generated.js +18 -0
- package/dist/vfs/dev-provider.d.ts +64 -0
- package/dist/vfs/dev-provider.d.ts.map +1 -0
- package/dist/vfs/dev-provider.js +191 -0
- package/dist/vfs/events.d.ts +51 -0
- package/dist/vfs/events.d.ts.map +1 -0
- package/dist/vfs/events.js +127 -0
- package/dist/vfs/path.d.ts +30 -0
- package/dist/vfs/path.d.ts.map +1 -0
- package/dist/vfs/path.js +41 -0
- package/dist/vfs/seed-project.d.ts +62 -0
- package/dist/vfs/seed-project.d.ts.map +1 -0
- package/dist/vfs/seed-project.js +1045 -0
- package/dist/vfs/sqlite-vfs.d.ts +435 -0
- package/dist/vfs/sqlite-vfs.d.ts.map +1 -0
- package/dist/vfs/sqlite-vfs.js +1670 -0
- package/dist/vfs/symlink-registry.d.ts +64 -0
- package/dist/vfs/symlink-registry.d.ts.map +1 -0
- package/dist/vfs/symlink-registry.js +137 -0
- package/dist/wrangler/nimbus-wrangler.d.ts +157 -0
- package/dist/wrangler/nimbus-wrangler.d.ts.map +1 -0
- package/dist/wrangler/nimbus-wrangler.js +931 -0
- package/package.json +77 -0
- package/public/_assets/cirrus-plugin-react.bundle.js +70740 -0
- package/public/_assets/esbuild-0.24.2.wasm +0 -0
- package/public/_assets/real-vite-bundle.js +101478 -0
- package/public/_assets/rollup.wasm +0 -0
- package/public/favicon.svg +8 -0
- package/public/index.html +565 -0
- package/public/s/index.html +2379 -0
- package/scripts/bundle-esbuild-wasm.mjs +244 -0
- package/scripts/bundle-facet-workers.mjs +137 -0
- package/scripts/bundle-git.mjs +118 -0
- package/scripts/bundle-npm-cjs.mjs +363 -0
- package/scripts/bundle-plugin-react.mjs +480 -0
- package/scripts/bundle-real-vite.mjs +959 -0
- package/scripts/bundle-runtime.mjs +600 -0
- package/scripts/bundle-tailwind-play.mjs +130 -0
- package/scripts/patch-lifo-ui.mjs +131 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* auth/middleware.ts — Request-time extract + verify glue.
|
|
3
|
+
*
|
|
4
|
+
* `extractBearerToken(request)` pulls the token from one of three places,
|
|
5
|
+
* in order of precedence:
|
|
6
|
+
* 1. `Authorization: Bearer <token>` header (canonical).
|
|
7
|
+
* 2. `?nimbus_token=<token>` query parameter (for `<iframe src>`).
|
|
8
|
+
* 3. `nimbus_token=<token>` cookie (for browser nav after the iframe
|
|
9
|
+
* sets it; the iframe can read its own cookie because cookies are
|
|
10
|
+
* scoped to the Nimbus origin, not the embedder's).
|
|
11
|
+
*
|
|
12
|
+
* The query/cookie fallbacks exist because `<iframe>` URLs are the
|
|
13
|
+
* canonical embed shape for `<NimbusTerminal>` and browsers don't let
|
|
14
|
+
* cross-origin iframes carry custom request headers on the initial
|
|
15
|
+
* navigation request. The cookie path is used for post-load nav inside
|
|
16
|
+
* the iframe (e.g. browser back/forward).
|
|
17
|
+
*
|
|
18
|
+
* NOTE: when the request comes from inside an `<iframe src="...?nimbus_token=...">`,
|
|
19
|
+
* we set the cookie on the response so subsequent in-iframe navigations
|
|
20
|
+
* don't need to re-pass the query param. See `setNimbusTokenCookie`.
|
|
21
|
+
*/
|
|
22
|
+
import { verifyNimbusToken } from './token.js';
|
|
23
|
+
import { NimbusAuthError, NimbusTokenMalformedError, NimbusScopeError, NimbusSessionPinError, } from './types.js';
|
|
24
|
+
/** Cookie name used for in-iframe token persistence. */
|
|
25
|
+
export const NIMBUS_TOKEN_COOKIE = 'nimbus_token';
|
|
26
|
+
/** Query parameter name. */
|
|
27
|
+
export const NIMBUS_TOKEN_QUERY = 'nimbus_token';
|
|
28
|
+
/**
|
|
29
|
+
* Pull a token from a Request. Returns null if absent (caller decides
|
|
30
|
+
* whether absence is an auth failure or just "anonymous").
|
|
31
|
+
*
|
|
32
|
+
* Precedence: Authorization header → query → cookie.
|
|
33
|
+
*/
|
|
34
|
+
export function extractBearerToken(request) {
|
|
35
|
+
// 1. Authorization header.
|
|
36
|
+
const auth = request.headers.get('Authorization');
|
|
37
|
+
if (auth) {
|
|
38
|
+
const m = auth.match(/^Bearer\s+(.+)$/i);
|
|
39
|
+
if (m)
|
|
40
|
+
return m[1];
|
|
41
|
+
}
|
|
42
|
+
// 2. Query parameter.
|
|
43
|
+
const url = new URL(request.url);
|
|
44
|
+
const q = url.searchParams.get(NIMBUS_TOKEN_QUERY);
|
|
45
|
+
if (q && q.length > 0)
|
|
46
|
+
return q;
|
|
47
|
+
// 3. Cookie.
|
|
48
|
+
const cookie = request.headers.get('Cookie');
|
|
49
|
+
if (cookie) {
|
|
50
|
+
for (const c of cookie.split(';')) {
|
|
51
|
+
const [k, v] = c.trim().split('=', 2);
|
|
52
|
+
if (k === NIMBUS_TOKEN_COOKIE && v)
|
|
53
|
+
return decodeURIComponent(v);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Verify a token from a Request. Convenience wrapper around
|
|
60
|
+
* {@link extractBearerToken} + {@link verifyNimbusToken}.
|
|
61
|
+
*
|
|
62
|
+
* @throws {NimbusTokenMalformedError} when no token is present and
|
|
63
|
+
* `requireToken` is true (default).
|
|
64
|
+
* @throws Any error from {@link verifyNimbusToken}.
|
|
65
|
+
*/
|
|
66
|
+
export async function verifyRequestToken(request, env, opts = {}) {
|
|
67
|
+
const token = extractBearerToken(request);
|
|
68
|
+
if (!token) {
|
|
69
|
+
if (opts.requireToken === false)
|
|
70
|
+
return null;
|
|
71
|
+
throw new NimbusTokenMalformedError('no Bearer token in Authorization header, nimbus_token query, or cookie');
|
|
72
|
+
}
|
|
73
|
+
return verifyNimbusToken(env, token);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Assert that the verified token carries every required scope.
|
|
77
|
+
*
|
|
78
|
+
* Tokens with `scopes === undefined` are treated as "all permitted"
|
|
79
|
+
* (legacy/full-trust). Tokens with an explicit `scopes` array must
|
|
80
|
+
* contain every entry in `required`.
|
|
81
|
+
*
|
|
82
|
+
* @throws {NimbusScopeError} on the first missing scope.
|
|
83
|
+
*/
|
|
84
|
+
export function requireScopes(verified, required) {
|
|
85
|
+
if (verified.claims.scopes === undefined)
|
|
86
|
+
return;
|
|
87
|
+
for (const r of required) {
|
|
88
|
+
if (!verified.claims.scopes.includes(r)) {
|
|
89
|
+
throw new NimbusScopeError(r);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Assert that a sid-pinned token matches the session being attached. No-op
|
|
95
|
+
* if the token isn't sid-pinned.
|
|
96
|
+
*
|
|
97
|
+
* @throws {NimbusSessionPinError} on mismatch.
|
|
98
|
+
*/
|
|
99
|
+
export function requireSessionPin(verified, attemptedSessionId) {
|
|
100
|
+
if (verified.claims.sid !== undefined && verified.claims.sid !== attemptedSessionId) {
|
|
101
|
+
throw new NimbusSessionPinError(verified.claims.sid, attemptedSessionId);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Build a Set-Cookie header value persisting the token in-iframe so
|
|
106
|
+
* subsequent navigations don't need the `?nimbus_token=` query.
|
|
107
|
+
*
|
|
108
|
+
* Cookie attributes:
|
|
109
|
+
* - HttpOnly: NO — JS in the iframe shell needs to forward the token
|
|
110
|
+
* to its WebSocket via subprotocol. Keep this in mind for XSS posture
|
|
111
|
+
* (Nimbus's xterm shell never executes embedder JS, mitigating risk).
|
|
112
|
+
* - Secure: YES (production).
|
|
113
|
+
* - SameSite=None: required because the iframe is cross-origin from
|
|
114
|
+
* the embedder's app. Pairs with Secure.
|
|
115
|
+
* - Path: scoped to `/s/` so non-session paths don't see the cookie.
|
|
116
|
+
* - Max-Age: matches the token's remaining lifetime.
|
|
117
|
+
*/
|
|
118
|
+
export function setNimbusTokenCookie(token, expSec, opts = {}) {
|
|
119
|
+
const secure = opts.secure !== false; // default true
|
|
120
|
+
const nowSec = Math.floor(Date.now() / 1000);
|
|
121
|
+
const maxAge = Math.max(0, expSec - nowSec);
|
|
122
|
+
const parts = [
|
|
123
|
+
`${NIMBUS_TOKEN_COOKIE}=${encodeURIComponent(token)}`,
|
|
124
|
+
'Path=/s',
|
|
125
|
+
`Max-Age=${maxAge}`,
|
|
126
|
+
'SameSite=None',
|
|
127
|
+
];
|
|
128
|
+
if (secure)
|
|
129
|
+
parts.push('Secure');
|
|
130
|
+
return parts.join('; ');
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Map a NimbusAuthError to a `Response` suitable for the embedder's
|
|
134
|
+
* `fetch` handler to return. JSON body with `{ error, code }`.
|
|
135
|
+
*/
|
|
136
|
+
export function authErrorResponse(e) {
|
|
137
|
+
if (e instanceof NimbusAuthError) {
|
|
138
|
+
return new Response(JSON.stringify({ error: e.message, code: e.code }), {
|
|
139
|
+
status: e.httpStatus,
|
|
140
|
+
headers: {
|
|
141
|
+
'Content-Type': 'application/json',
|
|
142
|
+
'Cache-Control': 'no-store',
|
|
143
|
+
// Hint to browsers that this is an auth challenge.
|
|
144
|
+
...(e.httpStatus === 401 ? { 'WWW-Authenticate': 'Bearer realm="nimbus"' } : {}),
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
// Unknown error — don't leak the message; log on caller side.
|
|
149
|
+
return new Response(JSON.stringify({ error: 'Internal auth error', code: 'E_AUTH_UNKNOWN' }), { status: 500, headers: { 'Content-Type': 'application/json', 'Cache-Control': 'no-store' } });
|
|
150
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* auth/token.ts — HS256 JWT issue + verify via WebCrypto subtle.
|
|
3
|
+
*
|
|
4
|
+
* Why HS256 (not RS256/ES256, not OAuth introspection)?
|
|
5
|
+
* - Embedder and Nimbus share `JWT_SECRET` via `wrangler secret put`.
|
|
6
|
+
* No external IDP needed; works on day-1 for any Workers project.
|
|
7
|
+
* - HMAC is constant-time-comparable, supported natively by WebCrypto,
|
|
8
|
+
* and zero-dependency.
|
|
9
|
+
* - Pattern matches Mossaic exactly — port surface (`scope`, `tn`,
|
|
10
|
+
* `sub?`, `sid?`, `iat`, `exp`) is identical except the `scope` value.
|
|
11
|
+
*
|
|
12
|
+
* Why WebCrypto over `jose`?
|
|
13
|
+
* - One fewer dependency in the SDK package. `jose` is great but pulls
|
|
14
|
+
* in ~70 KB of code for features we don't use (RSA, EC, JWE, JWK).
|
|
15
|
+
* - `crypto.subtle.{importKey,sign,verify}` is available identically in
|
|
16
|
+
* workerd, Node ≥ 18, and modern browsers. Test parity is excellent.
|
|
17
|
+
*
|
|
18
|
+
* Wire format:
|
|
19
|
+
*
|
|
20
|
+
* <b64url(header)>.<b64url(payload)>.<b64url(hmacSha256(secret, ".".join(...)))>
|
|
21
|
+
*
|
|
22
|
+
* `header` is constant `{"alg":"HS256","typ":"JWT"}`. We never read alg
|
|
23
|
+
* from the header at verify-time (eliminates the `alg: "none"` confusion
|
|
24
|
+
* attack class — see RFC 8725 §3.1).
|
|
25
|
+
*/
|
|
26
|
+
import { type VerifiedNimbusToken, type IssueTokenOptions } from './types.js';
|
|
27
|
+
/**
|
|
28
|
+
* The minimum env shape required by issue/verify. Embedders typically use
|
|
29
|
+
* `Env extends NimbusAuthEnv & ...other-bindings...` to compose.
|
|
30
|
+
*/
|
|
31
|
+
export interface NimbusAuthEnv {
|
|
32
|
+
/** HMAC secret. Set via `wrangler secret put JWT_SECRET`. */
|
|
33
|
+
JWT_SECRET: string;
|
|
34
|
+
/**
|
|
35
|
+
* Optional previous-generation secret for rotation. When set, verify
|
|
36
|
+
* accepts tokens signed by either. Issue always uses the primary.
|
|
37
|
+
*/
|
|
38
|
+
JWT_SECRET_PREVIOUS?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Mint a Nimbus JWT.
|
|
42
|
+
*
|
|
43
|
+
* @example Mint a 1-hour session token for a tenant user.
|
|
44
|
+
* ```ts
|
|
45
|
+
* import { issueNimbusToken } from '@nimbus-sh/sdk/token';
|
|
46
|
+
* const token = await issueNimbusToken(env, { tn: 'acme', sub: 'alice' });
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* @example Mint an admin token with `session:admin` scope, 24 hours.
|
|
50
|
+
* ```ts
|
|
51
|
+
* const token = await issueNimbusToken(
|
|
52
|
+
* env,
|
|
53
|
+
* { tn: 'acme', sub: 'ops', scopes: ['session:admin'] },
|
|
54
|
+
* { ttlMs: 24 * 60 * 60 * 1000 },
|
|
55
|
+
* );
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* @param env Object with at least `JWT_SECRET`.
|
|
59
|
+
* @param input Claims-without-iat/exp/scope. `scope` is set to `"nimbus"`
|
|
60
|
+
* by this function; do not pass it in.
|
|
61
|
+
* @param opts Optional `ttlMs` (default {@link DEFAULT_TOKEN_TTL_MS}, max
|
|
62
|
+
* {@link MAX_TOKEN_TTL_MS}) and `iatOverride` (tests only).
|
|
63
|
+
*
|
|
64
|
+
* @throws {NimbusAuthConfigError} when `env.JWT_SECRET` is missing/empty.
|
|
65
|
+
* @throws {NimbusTokenClaimsError} when `tn`/`sub` shape is invalid.
|
|
66
|
+
* @throws {NimbusTokenTtlError} when `opts.ttlMs` > {@link MAX_TOKEN_TTL_MS}.
|
|
67
|
+
*/
|
|
68
|
+
export declare function issueNimbusToken(env: NimbusAuthEnv, input: {
|
|
69
|
+
tn: string;
|
|
70
|
+
sub?: string;
|
|
71
|
+
scopes?: string[];
|
|
72
|
+
sid?: string;
|
|
73
|
+
}, opts?: IssueTokenOptions): Promise<string>;
|
|
74
|
+
/**
|
|
75
|
+
* Verify a Nimbus JWT and return the parsed claims + canonical DO name.
|
|
76
|
+
*
|
|
77
|
+
* @example Verify a token at request-time.
|
|
78
|
+
* ```ts
|
|
79
|
+
* import { verifyNimbusToken } from '@nimbus-sh/sdk/token';
|
|
80
|
+
* const { claims, doInstanceName } = await verifyNimbusToken(env, token);
|
|
81
|
+
* // → idFromName(`${doInstanceName}:${sessionId}`)
|
|
82
|
+
* ```
|
|
83
|
+
*
|
|
84
|
+
* @param env Object with `JWT_SECRET` and optional `JWT_SECRET_PREVIOUS`.
|
|
85
|
+
* @param token Raw JWT string (no `Bearer ` prefix — caller strips that).
|
|
86
|
+
*
|
|
87
|
+
* @throws {NimbusAuthConfigError} when `env.JWT_SECRET` is missing.
|
|
88
|
+
* @throws {NimbusTokenMalformedError} when token is not 3 b64 parts.
|
|
89
|
+
* @throws {NimbusTokenSignatureError} when HMAC verify fails for both
|
|
90
|
+
* primary and previous secrets.
|
|
91
|
+
* @throws {NimbusTokenClaimsError} when payload fields are missing or
|
|
92
|
+
* shape-invalid.
|
|
93
|
+
* @throws {NimbusTokenExpiredError} when `exp` < `now`.
|
|
94
|
+
*/
|
|
95
|
+
export declare function verifyNimbusToken(env: NimbusAuthEnv, token: string): Promise<VerifiedNimbusToken>;
|
|
96
|
+
//# sourceMappingURL=token.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../src/auth/token.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAWL,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACvB,MAAM,YAAY,CAAC;AAMpB;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,aAAa,EAClB,KAAK,EAAE;IACL,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,EACD,IAAI,GAAE,iBAAsB,GAC3B,OAAO,CAAC,MAAM,CAAC,CA8CjB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,aAAa,EAClB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,mBAAmB,CAAC,CAwE9B"}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* auth/token.ts — HS256 JWT issue + verify via WebCrypto subtle.
|
|
3
|
+
*
|
|
4
|
+
* Why HS256 (not RS256/ES256, not OAuth introspection)?
|
|
5
|
+
* - Embedder and Nimbus share `JWT_SECRET` via `wrangler secret put`.
|
|
6
|
+
* No external IDP needed; works on day-1 for any Workers project.
|
|
7
|
+
* - HMAC is constant-time-comparable, supported natively by WebCrypto,
|
|
8
|
+
* and zero-dependency.
|
|
9
|
+
* - Pattern matches Mossaic exactly — port surface (`scope`, `tn`,
|
|
10
|
+
* `sub?`, `sid?`, `iat`, `exp`) is identical except the `scope` value.
|
|
11
|
+
*
|
|
12
|
+
* Why WebCrypto over `jose`?
|
|
13
|
+
* - One fewer dependency in the SDK package. `jose` is great but pulls
|
|
14
|
+
* in ~70 KB of code for features we don't use (RSA, EC, JWE, JWK).
|
|
15
|
+
* - `crypto.subtle.{importKey,sign,verify}` is available identically in
|
|
16
|
+
* workerd, Node ≥ 18, and modern browsers. Test parity is excellent.
|
|
17
|
+
*
|
|
18
|
+
* Wire format:
|
|
19
|
+
*
|
|
20
|
+
* <b64url(header)>.<b64url(payload)>.<b64url(hmacSha256(secret, ".".join(...)))>
|
|
21
|
+
*
|
|
22
|
+
* `header` is constant `{"alg":"HS256","typ":"JWT"}`. We never read alg
|
|
23
|
+
* from the header at verify-time (eliminates the `alg: "none"` confusion
|
|
24
|
+
* attack class — see RFC 8725 §3.1).
|
|
25
|
+
*/
|
|
26
|
+
import { DEFAULT_TOKEN_TTL_MS, MAX_TOKEN_TTL_MS, ID_COMPONENT_RE, NimbusAuthConfigError, NimbusTokenMalformedError, NimbusTokenSignatureError, NimbusTokenClaimsError, NimbusTokenExpiredError, NimbusTokenTtlError, } from './types.js';
|
|
27
|
+
/** Constant JWT header. Serialized at module load; we just splice the cached b64. */
|
|
28
|
+
const HEADER_JSON = '{"alg":"HS256","typ":"JWT"}';
|
|
29
|
+
const HEADER_B64 = b64urlEncodeString(HEADER_JSON);
|
|
30
|
+
/**
|
|
31
|
+
* Mint a Nimbus JWT.
|
|
32
|
+
*
|
|
33
|
+
* @example Mint a 1-hour session token for a tenant user.
|
|
34
|
+
* ```ts
|
|
35
|
+
* import { issueNimbusToken } from '@nimbus-sh/sdk/token';
|
|
36
|
+
* const token = await issueNimbusToken(env, { tn: 'acme', sub: 'alice' });
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @example Mint an admin token with `session:admin` scope, 24 hours.
|
|
40
|
+
* ```ts
|
|
41
|
+
* const token = await issueNimbusToken(
|
|
42
|
+
* env,
|
|
43
|
+
* { tn: 'acme', sub: 'ops', scopes: ['session:admin'] },
|
|
44
|
+
* { ttlMs: 24 * 60 * 60 * 1000 },
|
|
45
|
+
* );
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* @param env Object with at least `JWT_SECRET`.
|
|
49
|
+
* @param input Claims-without-iat/exp/scope. `scope` is set to `"nimbus"`
|
|
50
|
+
* by this function; do not pass it in.
|
|
51
|
+
* @param opts Optional `ttlMs` (default {@link DEFAULT_TOKEN_TTL_MS}, max
|
|
52
|
+
* {@link MAX_TOKEN_TTL_MS}) and `iatOverride` (tests only).
|
|
53
|
+
*
|
|
54
|
+
* @throws {NimbusAuthConfigError} when `env.JWT_SECRET` is missing/empty.
|
|
55
|
+
* @throws {NimbusTokenClaimsError} when `tn`/`sub` shape is invalid.
|
|
56
|
+
* @throws {NimbusTokenTtlError} when `opts.ttlMs` > {@link MAX_TOKEN_TTL_MS}.
|
|
57
|
+
*/
|
|
58
|
+
export async function issueNimbusToken(env, input, opts = {}) {
|
|
59
|
+
if (!env || typeof env.JWT_SECRET !== 'string' || env.JWT_SECRET.length === 0) {
|
|
60
|
+
throw new NimbusAuthConfigError('JWT_SECRET is not configured (set via `wrangler secret put JWT_SECRET`)');
|
|
61
|
+
}
|
|
62
|
+
if (!ID_COMPONENT_RE.test(input.tn)) {
|
|
63
|
+
throw new NimbusTokenClaimsError(`tn must match ${ID_COMPONENT_RE} (got: ${JSON.stringify(input.tn)})`);
|
|
64
|
+
}
|
|
65
|
+
if (input.sub !== undefined && !ID_COMPONENT_RE.test(input.sub)) {
|
|
66
|
+
throw new NimbusTokenClaimsError(`sub must match ${ID_COMPONENT_RE} (got: ${JSON.stringify(input.sub)})`);
|
|
67
|
+
}
|
|
68
|
+
if (input.sid !== undefined && !ID_COMPONENT_RE.test(input.sid)) {
|
|
69
|
+
throw new NimbusTokenClaimsError(`sid must match ${ID_COMPONENT_RE} (got: ${JSON.stringify(input.sid)})`);
|
|
70
|
+
}
|
|
71
|
+
const ttlMs = opts.ttlMs ?? DEFAULT_TOKEN_TTL_MS;
|
|
72
|
+
if (ttlMs <= 0) {
|
|
73
|
+
throw new NimbusTokenTtlError(ttlMs, MAX_TOKEN_TTL_MS);
|
|
74
|
+
}
|
|
75
|
+
if (ttlMs > MAX_TOKEN_TTL_MS) {
|
|
76
|
+
throw new NimbusTokenTtlError(ttlMs, MAX_TOKEN_TTL_MS);
|
|
77
|
+
}
|
|
78
|
+
const iat = opts.iatOverride ?? Math.floor(Date.now() / 1000);
|
|
79
|
+
const exp = iat + Math.floor(ttlMs / 1000);
|
|
80
|
+
const claims = {
|
|
81
|
+
scope: 'nimbus',
|
|
82
|
+
tn: input.tn,
|
|
83
|
+
...(input.sub !== undefined && { sub: input.sub }),
|
|
84
|
+
...(input.scopes !== undefined && { scopes: input.scopes }),
|
|
85
|
+
...(input.sid !== undefined && { sid: input.sid }),
|
|
86
|
+
iat,
|
|
87
|
+
exp,
|
|
88
|
+
};
|
|
89
|
+
const payloadB64 = b64urlEncodeString(JSON.stringify(claims));
|
|
90
|
+
const signingInput = `${HEADER_B64}.${payloadB64}`;
|
|
91
|
+
const sig = await hmacSha256(env.JWT_SECRET, signingInput);
|
|
92
|
+
return `${signingInput}.${b64urlEncodeBytes(sig)}`;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Verify a Nimbus JWT and return the parsed claims + canonical DO name.
|
|
96
|
+
*
|
|
97
|
+
* @example Verify a token at request-time.
|
|
98
|
+
* ```ts
|
|
99
|
+
* import { verifyNimbusToken } from '@nimbus-sh/sdk/token';
|
|
100
|
+
* const { claims, doInstanceName } = await verifyNimbusToken(env, token);
|
|
101
|
+
* // → idFromName(`${doInstanceName}:${sessionId}`)
|
|
102
|
+
* ```
|
|
103
|
+
*
|
|
104
|
+
* @param env Object with `JWT_SECRET` and optional `JWT_SECRET_PREVIOUS`.
|
|
105
|
+
* @param token Raw JWT string (no `Bearer ` prefix — caller strips that).
|
|
106
|
+
*
|
|
107
|
+
* @throws {NimbusAuthConfigError} when `env.JWT_SECRET` is missing.
|
|
108
|
+
* @throws {NimbusTokenMalformedError} when token is not 3 b64 parts.
|
|
109
|
+
* @throws {NimbusTokenSignatureError} when HMAC verify fails for both
|
|
110
|
+
* primary and previous secrets.
|
|
111
|
+
* @throws {NimbusTokenClaimsError} when payload fields are missing or
|
|
112
|
+
* shape-invalid.
|
|
113
|
+
* @throws {NimbusTokenExpiredError} when `exp` < `now`.
|
|
114
|
+
*/
|
|
115
|
+
export async function verifyNimbusToken(env, token) {
|
|
116
|
+
if (!env || typeof env.JWT_SECRET !== 'string' || env.JWT_SECRET.length === 0) {
|
|
117
|
+
throw new NimbusAuthConfigError('JWT_SECRET is not configured (set via `wrangler secret put JWT_SECRET`)');
|
|
118
|
+
}
|
|
119
|
+
if (typeof token !== 'string' || token.length === 0) {
|
|
120
|
+
throw new NimbusTokenMalformedError('token must be a non-empty string');
|
|
121
|
+
}
|
|
122
|
+
const parts = token.split('.');
|
|
123
|
+
if (parts.length !== 3) {
|
|
124
|
+
throw new NimbusTokenMalformedError(`expected 3 dot-separated parts, got ${parts.length}`);
|
|
125
|
+
}
|
|
126
|
+
const [headerB64, payloadB64, sigB64] = parts;
|
|
127
|
+
const signingInput = `${headerB64}.${payloadB64}`;
|
|
128
|
+
// Verify against primary, then previous (for rotation windows).
|
|
129
|
+
const sigBytes = b64urlDecodeBytes(sigB64);
|
|
130
|
+
const okPrimary = await hmacVerify(env.JWT_SECRET, signingInput, sigBytes);
|
|
131
|
+
if (!okPrimary) {
|
|
132
|
+
if (env.JWT_SECRET_PREVIOUS) {
|
|
133
|
+
const okPrev = await hmacVerify(env.JWT_SECRET_PREVIOUS, signingInput, sigBytes);
|
|
134
|
+
if (!okPrev)
|
|
135
|
+
throw new NimbusTokenSignatureError();
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
throw new NimbusTokenSignatureError();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// Decode + claim-shape validate.
|
|
142
|
+
let claims;
|
|
143
|
+
try {
|
|
144
|
+
claims = JSON.parse(b64urlDecodeString(payloadB64));
|
|
145
|
+
}
|
|
146
|
+
catch (e) {
|
|
147
|
+
throw new NimbusTokenMalformedError(`payload is not valid JSON: ${e?.message || e}`);
|
|
148
|
+
}
|
|
149
|
+
if (claims === null || typeof claims !== 'object') {
|
|
150
|
+
throw new NimbusTokenClaimsError('payload is not a JSON object');
|
|
151
|
+
}
|
|
152
|
+
if (claims.scope !== 'nimbus') {
|
|
153
|
+
throw new NimbusTokenClaimsError(`scope must be "nimbus" (got: ${JSON.stringify(claims.scope)})`);
|
|
154
|
+
}
|
|
155
|
+
if (typeof claims.tn !== 'string' || !ID_COMPONENT_RE.test(claims.tn)) {
|
|
156
|
+
throw new NimbusTokenClaimsError(`tn is missing or invalid`);
|
|
157
|
+
}
|
|
158
|
+
if (claims.sub !== undefined && (typeof claims.sub !== 'string' || !ID_COMPONENT_RE.test(claims.sub))) {
|
|
159
|
+
throw new NimbusTokenClaimsError(`sub shape invalid`);
|
|
160
|
+
}
|
|
161
|
+
if (claims.sid !== undefined && (typeof claims.sid !== 'string' || !ID_COMPONENT_RE.test(claims.sid))) {
|
|
162
|
+
throw new NimbusTokenClaimsError(`sid shape invalid`);
|
|
163
|
+
}
|
|
164
|
+
if (typeof claims.iat !== 'number' || typeof claims.exp !== 'number') {
|
|
165
|
+
throw new NimbusTokenClaimsError(`iat and exp must be numbers (NumericDate)`);
|
|
166
|
+
}
|
|
167
|
+
if (claims.scopes !== undefined) {
|
|
168
|
+
if (!Array.isArray(claims.scopes) || claims.scopes.some((s) => typeof s !== 'string')) {
|
|
169
|
+
throw new NimbusTokenClaimsError(`scopes must be an array of strings`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
const nowSec = Math.floor(Date.now() / 1000);
|
|
173
|
+
if (claims.exp < nowSec) {
|
|
174
|
+
throw new NimbusTokenExpiredError(nowSec - claims.exp);
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
claims,
|
|
178
|
+
doInstanceName: `${claims.tn}:${claims.sub ?? '_'}`,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
// ── Internal helpers ─────────────────────────────────────────────────────
|
|
182
|
+
/**
|
|
183
|
+
* HMAC-SHA-256 sign via WebCrypto subtle. Returns raw signature bytes.
|
|
184
|
+
*
|
|
185
|
+
* The CryptoKey is created on each call. workerd's subtle is cheap enough
|
|
186
|
+
* that caching would only matter at >10K signs/sec; we're at single-digit
|
|
187
|
+
* per request.
|
|
188
|
+
*/
|
|
189
|
+
async function hmacSha256(secret, data) {
|
|
190
|
+
const key = await crypto.subtle.importKey('raw', new TextEncoder().encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
|
191
|
+
const sig = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(data));
|
|
192
|
+
return new Uint8Array(sig);
|
|
193
|
+
}
|
|
194
|
+
/** Constant-time verify via `crypto.subtle.verify` (no manual loop needed). */
|
|
195
|
+
async function hmacVerify(secret, data, expected) {
|
|
196
|
+
const key = await crypto.subtle.importKey('raw', new TextEncoder().encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['verify']);
|
|
197
|
+
return crypto.subtle.verify('HMAC', key, expected, new TextEncoder().encode(data));
|
|
198
|
+
}
|
|
199
|
+
/** base64url-encode a UTF-8 string. No padding. */
|
|
200
|
+
function b64urlEncodeString(s) {
|
|
201
|
+
return b64urlEncodeBytes(new TextEncoder().encode(s));
|
|
202
|
+
}
|
|
203
|
+
/** base64url-encode raw bytes. No padding (per RFC 7515 §2). */
|
|
204
|
+
function b64urlEncodeBytes(bytes) {
|
|
205
|
+
// workerd has btoa; convert bytes -> binary string -> btoa -> url-safe -> strip pad.
|
|
206
|
+
let binary = '';
|
|
207
|
+
for (let i = 0; i < bytes.length; i++)
|
|
208
|
+
binary += String.fromCharCode(bytes[i]);
|
|
209
|
+
return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
210
|
+
}
|
|
211
|
+
/** base64url-decode to a UTF-8 string. */
|
|
212
|
+
function b64urlDecodeString(s) {
|
|
213
|
+
return new TextDecoder().decode(b64urlDecodeBytes(s));
|
|
214
|
+
}
|
|
215
|
+
/** base64url-decode to raw bytes. */
|
|
216
|
+
function b64urlDecodeBytes(s) {
|
|
217
|
+
// Re-pad to a multiple of 4 + restore +/.
|
|
218
|
+
const padded = s.replace(/-/g, '+').replace(/_/g, '/');
|
|
219
|
+
const pad = (4 - (padded.length % 4)) % 4;
|
|
220
|
+
const padStr = padded + '='.repeat(pad);
|
|
221
|
+
const binary = atob(padStr);
|
|
222
|
+
const out = new Uint8Array(binary.length);
|
|
223
|
+
for (let i = 0; i < binary.length; i++)
|
|
224
|
+
out[i] = binary.charCodeAt(i);
|
|
225
|
+
return out;
|
|
226
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* auth/types.ts — Public-facing types for the Nimbus auth surface.
|
|
3
|
+
*
|
|
4
|
+
* Two consumers:
|
|
5
|
+
* 1. The Worker (`@nimbus-sh/worker`) — reads `verifyNimbusToken` results
|
|
6
|
+
* to gate `/s/<id>/` routes and pin DO instance names per-tenant.
|
|
7
|
+
* 2. The SDK (`@nimbus-sh/sdk`) — re-exports `issueNimbusToken` so an
|
|
8
|
+
* embedder's Worker (or CLI) can mint tokens from a JWT secret.
|
|
9
|
+
*
|
|
10
|
+
* The on-the-wire shape is a JWS with HS256 signature, base64url-encoded:
|
|
11
|
+
*
|
|
12
|
+
* <header>.<payload>.<signature>
|
|
13
|
+
*
|
|
14
|
+
* where the payload is a `NimbusTokenClaims` JSON object below. The header
|
|
15
|
+
* is the constant `{"alg":"HS256","typ":"JWT"}` — we don't permit alg
|
|
16
|
+
* variation (no `alg: "none"` confusion attack; we never read the header's
|
|
17
|
+
* `alg`).
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Claims encoded inside the JWT payload. Mirrors Mossaic's `{scope, ns, tn,
|
|
21
|
+
* sub?, sid?, iat, exp}` exactly except:
|
|
22
|
+
* - `scope` is the literal string `"nimbus"`. Cross-product tokens
|
|
23
|
+
* (e.g. a Mossaic VFS token) MUST be rejected by Nimbus's verifier.
|
|
24
|
+
* - `tn` is the tenant. Required. Maps to the `tenant` portion of the
|
|
25
|
+
* DO instance name. Per `[A-Za-z0-9._-]{1,128}` (validated at verify).
|
|
26
|
+
* - `sub` is the user/principal within the tenant. Optional; when
|
|
27
|
+
* absent, treated as `"_"` for DO naming (i.e. the tenant-default
|
|
28
|
+
* user).
|
|
29
|
+
* - `scopes` is an optional array of capability strings the token
|
|
30
|
+
* grants. v1 honors `"session:create"`, `"session:attach"`,
|
|
31
|
+
* `"session:admin"`. Absent = all permitted (backward compat for
|
|
32
|
+
* legacy tokens).
|
|
33
|
+
* - `sid` is an optional session-pin: when present, the token may only
|
|
34
|
+
* attach to that single session. When absent, the token may attach
|
|
35
|
+
* to any session for `(tn, sub)`.
|
|
36
|
+
* - `iat` / `exp` are UNIX seconds (NumericDate per RFC 7519).
|
|
37
|
+
*/
|
|
38
|
+
export interface NimbusTokenClaims {
|
|
39
|
+
/** MUST be the literal `"nimbus"`. Discriminator vs Mossaic / other JWTs. */
|
|
40
|
+
scope: 'nimbus';
|
|
41
|
+
/** Tenant identifier. `[A-Za-z0-9._-]{1,128}`. */
|
|
42
|
+
tn: string;
|
|
43
|
+
/** Subject (user). Optional; absent = tenant-default. `[A-Za-z0-9._-]{1,128}`. */
|
|
44
|
+
sub?: string;
|
|
45
|
+
/** Capability scopes. Absent = legacy "all permitted". */
|
|
46
|
+
scopes?: string[];
|
|
47
|
+
/** Session-pin: token only valid for this session. Optional. */
|
|
48
|
+
sid?: string;
|
|
49
|
+
/** Issued-at (UNIX seconds). */
|
|
50
|
+
iat: number;
|
|
51
|
+
/** Expiry (UNIX seconds). */
|
|
52
|
+
exp: number;
|
|
53
|
+
}
|
|
54
|
+
/** Default token TTL when `issueNimbusToken` is called without `ttlMs`. 1 hour. */
|
|
55
|
+
export declare const DEFAULT_TOKEN_TTL_MS: number;
|
|
56
|
+
/** Maximum permitted token TTL. 30 days. Prevents accidental long-lived secrets. */
|
|
57
|
+
export declare const MAX_TOKEN_TTL_MS: number;
|
|
58
|
+
/**
|
|
59
|
+
* Result of a successful `verifyNimbusToken` call. Verified claims plus a
|
|
60
|
+
* canonical DO instance-naming string derived from `(tn, sub || '_')`.
|
|
61
|
+
*
|
|
62
|
+
* Consumers should prefer `doInstanceName` over re-deriving from `claims`
|
|
63
|
+
* — the verifier is the single source of truth for sub-defaulting.
|
|
64
|
+
*/
|
|
65
|
+
export interface VerifiedNimbusToken {
|
|
66
|
+
/** The verified claims, unchanged from the wire format. */
|
|
67
|
+
claims: NimbusTokenClaims;
|
|
68
|
+
/** `${tn}:${sub || '_'}` — feed to `idFromName` after appending sessionId. */
|
|
69
|
+
doInstanceName: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Options for `issueNimbusToken`. All optional.
|
|
73
|
+
*
|
|
74
|
+
* `ttlMs` defaults to {@link DEFAULT_TOKEN_TTL_MS}. Values > {@link
|
|
75
|
+
* MAX_TOKEN_TTL_MS} throw `NimbusAuthError` with `code: 'TTL_TOO_LARGE'`.
|
|
76
|
+
*/
|
|
77
|
+
export interface IssueTokenOptions {
|
|
78
|
+
ttlMs?: number;
|
|
79
|
+
/** Override `iat`. Useful in tests to assert exp arithmetic. */
|
|
80
|
+
iatOverride?: number;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Base error class for every auth-layer failure. Subclasses encode the
|
|
84
|
+
* specific failure mode in `code`.
|
|
85
|
+
*
|
|
86
|
+
* Why a class hierarchy instead of an `{ ok: false, code }` union? Two
|
|
87
|
+
* reasons: (1) embedders' error handlers naturally use `try/catch`, and
|
|
88
|
+
* an exception flow propagates without ceremony; (2) consumers in
|
|
89
|
+
* non-TypeScript environments (a Node SSR layer minting tokens) still
|
|
90
|
+
* get `error.code` introspection.
|
|
91
|
+
*
|
|
92
|
+
* Every subclass below is INSTANCEOF-compatible with this base, so a
|
|
93
|
+
* blanket `catch (e instanceof NimbusAuthError)` works.
|
|
94
|
+
*/
|
|
95
|
+
export declare class NimbusAuthError extends Error {
|
|
96
|
+
/** Machine-readable code for catch sites. Subclass-stable. */
|
|
97
|
+
readonly code: string;
|
|
98
|
+
/** HTTP status the embedder's auth gate should map to (typically 401). */
|
|
99
|
+
readonly httpStatus: number;
|
|
100
|
+
constructor(message: string, code: string, httpStatus?: number);
|
|
101
|
+
}
|
|
102
|
+
/** Thrown when JWT_SECRET env var is missing at issue/verify time. */
|
|
103
|
+
export declare class NimbusAuthConfigError extends NimbusAuthError {
|
|
104
|
+
constructor(message?: string);
|
|
105
|
+
}
|
|
106
|
+
/** Thrown when the token shape is structurally invalid (not 3 parts, bad b64). */
|
|
107
|
+
export declare class NimbusTokenMalformedError extends NimbusAuthError {
|
|
108
|
+
constructor(reason: string);
|
|
109
|
+
}
|
|
110
|
+
/** Thrown when the HMAC signature does not verify against JWT_SECRET. */
|
|
111
|
+
export declare class NimbusTokenSignatureError extends NimbusAuthError {
|
|
112
|
+
constructor();
|
|
113
|
+
}
|
|
114
|
+
/** Thrown when claims fail validation (missing scope, bad tn shape, etc.). */
|
|
115
|
+
export declare class NimbusTokenClaimsError extends NimbusAuthError {
|
|
116
|
+
constructor(reason: string);
|
|
117
|
+
}
|
|
118
|
+
/** Thrown when `exp` is in the past (with no clock skew tolerance — strict). */
|
|
119
|
+
export declare class NimbusTokenExpiredError extends NimbusAuthError {
|
|
120
|
+
/** Seconds the token expired ago (always ≥ 0). */
|
|
121
|
+
readonly expiredAgoSec: number;
|
|
122
|
+
constructor(expiredAgoSec: number);
|
|
123
|
+
}
|
|
124
|
+
/** Thrown when issue-time ttl exceeds {@link MAX_TOKEN_TTL_MS}. */
|
|
125
|
+
export declare class NimbusTokenTtlError extends NimbusAuthError {
|
|
126
|
+
constructor(ttlMs: number, maxMs: number);
|
|
127
|
+
}
|
|
128
|
+
/** Thrown when a route requires a scope the token doesn't carry. */
|
|
129
|
+
export declare class NimbusScopeError extends NimbusAuthError {
|
|
130
|
+
readonly requiredScope: string;
|
|
131
|
+
constructor(requiredScope: string);
|
|
132
|
+
}
|
|
133
|
+
/** Thrown when sid-pin doesn't match the actual session being attached. */
|
|
134
|
+
export declare class NimbusSessionPinError extends NimbusAuthError {
|
|
135
|
+
readonly pinnedTo: string;
|
|
136
|
+
readonly attempted: string;
|
|
137
|
+
constructor(pinnedTo: string, attempted: string);
|
|
138
|
+
}
|
|
139
|
+
/** Regex used to validate the `tn` and `sub` shape (per Mossaic §7.1). */
|
|
140
|
+
export declare const ID_COMPONENT_RE: RegExp;
|
|
141
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/auth/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,iBAAiB;IAChC,6EAA6E;IAC7E,KAAK,EAAE,QAAQ,CAAC;IAChB,kDAAkD;IAClD,EAAE,EAAE,MAAM,CAAC;IACX,kFAAkF;IAClF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,gEAAgE;IAChE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,6BAA6B;IAC7B,GAAG,EAAE,MAAM,CAAC;CACb;AAED,mFAAmF;AACnF,eAAO,MAAM,oBAAoB,QAAiB,CAAC;AAEnD,oFAAoF;AACpF,eAAO,MAAM,gBAAgB,QAA2B,CAAC;AAEzD;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC,2DAA2D;IAC3D,MAAM,EAAE,iBAAiB,CAAC;IAC1B,8EAA8E;IAC9E,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,eAAgB,SAAQ,KAAK;IACxC,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;gBAChB,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,GAAE,MAAY;CAMpE;AAED,sEAAsE;AACtE,qBAAa,qBAAsB,SAAQ,eAAe;gBAC5C,OAAO,GAAE,MAAuC;CAI7D;AAED,kFAAkF;AAClF,qBAAa,yBAA0B,SAAQ,eAAe;gBAChD,MAAM,EAAE,MAAM;CAI3B;AAED,yEAAyE;AACzE,qBAAa,yBAA0B,SAAQ,eAAe;;CAK7D;AAED,8EAA8E;AAC9E,qBAAa,sBAAuB,SAAQ,eAAe;gBAC7C,MAAM,EAAE,MAAM;CAI3B;AAED,gFAAgF;AAChF,qBAAa,uBAAwB,SAAQ,eAAe;IAC1D,kDAAkD;IAClD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;gBACnB,aAAa,EAAE,MAAM;CAKlC;AAED,mEAAmE;AACnE,qBAAa,mBAAoB,SAAQ,eAAe;gBAC1C,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;CAQzC;AAED,oEAAoE;AACpE,qBAAa,gBAAiB,SAAQ,eAAe;IACnD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;gBACnB,aAAa,EAAE,MAAM;CAKlC;AAED,2EAA2E;AAC3E,qBAAa,qBAAsB,SAAQ,eAAe;IACxD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;gBACf,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;CAUhD;AAED,0EAA0E;AAC1E,eAAO,MAAM,eAAe,QAA4B,CAAC"}
|