Skip to content

feat(node): full dashboard auth and API parity#385

Merged
pratyush618 merged 21 commits into
masterfrom
feat/node-dashboard-parity
Jul 7, 2026
Merged

feat(node): full dashboard auth and API parity#385
pratyush618 merged 21 commits into
masterfrom
feat/node-dashboard-parity

Conversation

@pratyush618

@pratyush618 pratyush618 commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Closes the Node dashboard gap: real session auth + OAuth + the full set of missing dashboard APIs (previously only a shared-token gate and a partial API surface)

Changes

Session auth

  • First-run setup flow, PBKDF2 (600k) password login, KV-backed sessions, CSRF double-submit
  • Admin/viewer roles, env bootstrap admin (TASKITO_DASHBOARD_ADMIN_USER/PASSWORD)
  • CLI --token / --insecure-cookies flags, Express/Fastify mount options
  • Cross-SDK KV key layout + hash format, golden vector pinned in tests

OAuth

  • Google / GitHub / generic OIDC slots, PKCE S256
  • id_token validation on node:crypto (RS/PS/ES families)
  • Single-use KV state store, domain/org allowlists, admin-email role mapping
  • TASKITO_DASHBOARD_OAUTH_* env config, no new runtime dependencies

New endpoints

  • Settings CRUD, /health, /readiness, Prometheus /metrics (optional bearer gate)
  • KEDA scaler payload, /api/resources from worker heartbeats
  • Job errors/logs/replay/replay-history/DAG, /api/logs, /api/circuit-breakers
  • Dead-letter purge, webhook delivery detail/replay/rotate-secret
  • Task/queue overrides, per-task middleware toggles
  • Proxy + interception stats

Native additions (crates/taskito-node)

  • queryTaskLogs, listCircuitBreakers, replayJob, getReplayHistory, jobDag napi bindings

Persistence

  • Webhook delivery history moved from an in-memory ring to the settings KV (survives restarts)

Refactor

  • Dashboard package reorganised into auth/, handlers/, stores/ subpackages with barrels
  • Route policy split out, typed error hierarchy

Behavior change

Session auth is now the default. With zero users, the API returns 503 setup_required and the SPA shows the setup flow. The legacy shared-token mode (auth: {token}) is unchanged for existing deployments.

Test plan

  • 349 vitest tests green (55 files), incl. new suites for session auth, OAuth (unit + endpoint), settings/ops, overrides/middleware, deliveries, and native inspection routes
  • cargo fmt / clippy clean

Summary by CodeRabbit

  • New Features
    • Added session-based dashboard auth with setup, login/logout, password changes, and OAuth (including PKCE and safe redirects).
    • Expanded dashboard APIs for task logs, circuit breakers, replay history, job DAGs, middleware toggles, overrides, settings, and webhook delivery management (replay, secret rotation, persisted delivery history).
    • Added operational endpoints: health/readiness, KEDA scaler payloads, proxy/interception metrics, and resource status.
  • Bug Fixes
    • Improved webhook delivery listing/filters with input validation, plus safer replay behavior.
  • Chores
    • Updated Node SDK/Express/Fastify dashboard auth options (token gate + secure cookie toggle) and refreshed dashboard server wiring; added integration tests.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 15 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dcbf1c2d-3886-4056-8aa8-379d8fed97f7

📥 Commits

Reviewing files that changed from the base of the PR and between 756847e and 84d2e7b.

📒 Files selected for processing (62)
  • crates/taskito-node/src/convert/mod.rs
  • crates/taskito-node/src/convert/ops.rs
  • crates/taskito-node/src/queue/admin.rs
  • crates/taskito-node/src/queue/inspect.rs
  • sdks/node/src/cli/commands/dashboard.ts
  • sdks/node/src/contrib/express.ts
  • sdks/node/src/contrib/fastify.ts
  • sdks/node/src/dashboard/api.ts
  • sdks/node/src/dashboard/auth/context.ts
  • sdks/node/src/dashboard/auth/handlers.ts
  • sdks/node/src/dashboard/auth/index.ts
  • sdks/node/src/dashboard/auth/oauth/config.ts
  • sdks/node/src/dashboard/auth/oauth/flow.ts
  • sdks/node/src/dashboard/auth/oauth/idToken.ts
  • sdks/node/src/dashboard/auth/oauth/identity.ts
  • sdks/node/src/dashboard/auth/oauth/index.ts
  • sdks/node/src/dashboard/auth/oauth/pkce.ts
  • sdks/node/src/dashboard/auth/oauth/providers.ts
  • sdks/node/src/dashboard/auth/oauth/stateStore.ts
  • sdks/node/src/dashboard/auth/store.ts
  • sdks/node/src/dashboard/auth/tokenAuth.ts
  • sdks/node/src/dashboard/errors.ts
  • sdks/node/src/dashboard/handlers/contract.ts
  • sdks/node/src/dashboard/handlers/core.ts
  • sdks/node/src/dashboard/handlers/index.ts
  • sdks/node/src/dashboard/handlers/metrics.ts
  • sdks/node/src/dashboard/handlers/middleware.ts
  • sdks/node/src/dashboard/handlers/ops.ts
  • sdks/node/src/dashboard/handlers/overrides.ts
  • sdks/node/src/dashboard/handlers/settings.ts
  • sdks/node/src/dashboard/index.ts
  • sdks/node/src/dashboard/policy.ts
  • sdks/node/src/dashboard/routes.ts
  • sdks/node/src/dashboard/server.ts
  • sdks/node/src/dashboard/stores/index.ts
  • sdks/node/src/dashboard/stores/middlewareDisables.ts
  • sdks/node/src/dashboard/stores/overrides.ts
  • sdks/node/src/dashboard/testing.ts
  • sdks/node/src/dashboard/urlSafety.ts
  • sdks/node/src/index.ts
  • sdks/node/src/interception.ts
  • sdks/node/src/middleware.ts
  • sdks/node/src/native.ts
  • sdks/node/src/proxies/index.ts
  • sdks/node/src/proxies/metrics.ts
  • sdks/node/src/proxies/proxies.ts
  • sdks/node/src/proxies/proxy-session.ts
  • sdks/node/src/queue.ts
  • sdks/node/src/types.ts
  • sdks/node/src/webhooks/deliveryLog.ts
  • sdks/node/src/webhooks/manager.ts
  • sdks/node/src/worker.ts
  • sdks/node/test/dashboard/inspectRoutes.test.ts
  • sdks/node/test/dashboard/oauth.test.ts
  • sdks/node/test/dashboard/oauthEndpoints.test.ts
  • sdks/node/test/dashboard/opsSettings.test.ts
  • sdks/node/test/dashboard/overridesMiddleware.test.ts
  • sdks/node/test/dashboard/server.test.ts
  • sdks/node/test/dashboard/sessionAuth.test.ts
  • sdks/node/test/dashboard/webhookDeliveries.test.ts
  • sdks/node/test/integrations/express.test.ts
  • sdks/node/test/integrations/fastify.test.ts
📝 Walkthrough

Walkthrough

This PR adds Rust N-API support for job replay and inspection, and expands the Node dashboard with session/CSRF auth, optional OAuth login, ops and settings endpoints, override and middleware controls, webhook delivery replay, and proxy/interception metrics. It also rewires server dispatch, queue behavior, worker startup, CLI integrations, and tests around those surfaces.

Changes

Dashboard auth, inspection, and admin surface

Layer / File(s) Summary
Rust replay and inspection bindings
crates/taskito-node/src/convert/mod.rs, crates/taskito-node/src/convert/ops.rs, crates/taskito-node/src/queue/admin.rs, crates/taskito-node/src/queue/inspect.rs
Adds JS-facing conversion types and N-API methods for job replay, circuit breaker listing, replay history, DAG traversal, and task log querying.
Native type re-exports
sdks/node/src/native.ts, sdks/node/src/types.ts
Re-exports CircuitBreaker, DagEdge, JobDag, and ReplayEntry from native bindings.
Auth store, errors, and request context
sdks/node/src/dashboard/auth/store.ts, sdks/node/src/dashboard/auth/context.ts, sdks/node/src/dashboard/errors.ts, sdks/node/src/dashboard/urlSafety.ts
Adds password/session persistence, admin bootstrap, error classes, per-request auth context, CSRF checks, and redirect safety validation.
Auth endpoint handlers
sdks/node/src/dashboard/auth/handlers.ts, sdks/node/src/dashboard/auth/index.ts
Adds setup/login/logout/whoami/changePassword handlers and auth barrel exports.
OAuth login flow
sdks/node/src/dashboard/auth/oauth/*
Implements OAuth config parsing, PKCE, id token validation, provider implementations, state storage, and OAuth flow orchestration.
Server dispatch, policy, and routes
sdks/node/src/dashboard/policy.ts, sdks/node/src/dashboard/server.ts, sdks/node/src/dashboard/routes.ts, sdks/node/src/dashboard/index.ts
Restructures dashboard dispatch for session/token modes, adds OAuth and probe handling, updates route signatures, and wires new server options and exports.
Dashboard handler modules
sdks/node/src/dashboard/handlers/*
Adds contract mappers, ops endpoints, protected settings, task/queue overrides, middleware toggles, and webhook/job inspection handlers.
Overrides, middleware disables, and queue wiring
sdks/node/src/dashboard/stores/*, sdks/node/src/worker.ts, sdks/node/src/queue.ts
Persists overrides and middleware disables, applies them in worker startup, and exposes the related queue/admin APIs.
Proxy and interception metrics
sdks/node/src/proxies/*, sdks/node/src/interception.ts, sdks/node/src/queue.ts
Adds proxy/interception metrics and wires them into reconstruction, cleanup, and interceptor execution.
Webhook delivery persistence and replay
sdks/node/src/webhooks/deliveryLog.ts, sdks/node/src/webhooks/manager.ts
Adds persisted delivery logs, replay, and secret rotation.
CLI/Express/Fastify wiring and testing seam
sdks/node/src/cli/commands/dashboard.ts, sdks/node/src/contrib/express.ts, sdks/node/src/contrib/fastify.ts, sdks/node/src/dashboard/testing.ts
Adds token and cookie options and a helper for seeded dashboard sessions in tests.
Tests
sdks/node/test/dashboard/*, sdks/node/test/integrations/*
Adds and updates tests for auth, OAuth, ops, overrides, middleware, webhook deliveries, inspect routes, and dashboard server wiring.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • ByteVeda/taskito#268: Touches the same Rust N-API conversion and queue inspection surface that this PR extends with replay, DAG, and circuit-breaker support.
  • ByteVeda/taskito#367: Adds interception metrics plumbing that this PR builds on with additional queue and proxy metrics wiring.

Suggested labels: rust, storage, tests

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: Node dashboard auth plus broad API parity additions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/node-dashboard-parity

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

Comment thread sdks/node/src/dashboard/auth/oauth/config.ts Fixed

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 17

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
sdks/node/src/proxies/proxy-session.ts (1)

71-76: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Record verification failures in session reconstruction metrics.

ProxySession.reconstruct bypasses Proxies.reconstruct, so signature/expiry/purpose failures from verifyRef are not counted while the direct path records them.

📊 Proposed fix
     this.ensureOpen();
     const handler = this.proxies.handlerFor(ref.handler);
-    this.proxies.verifyRef(ref, expectedPurpose);
+    try {
+      this.proxies.verifyRef(ref, expectedPurpose);
+    } catch (error) {
+      proxyMetrics.recordChecksumFailure(ref.handler);
+      throw error;
+    }
     if (this.reconstructed.has(ref.signature)) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdks/node/src/proxies/proxy-session.ts` around lines 71 - 76,
ProxySession.reconstruct currently calls this.proxies.verifyRef directly, so
signature/expiry/purpose failures are not recorded in the session reconstruction
metrics like they are through Proxies.reconstruct. Update
ProxySession.reconstruct to route verification through the same metrics-tracking
path, or explicitly record verification failures around verifyRef using the
existing reconstruction metrics helpers. Keep the behavior for successful
reconstruction and the reconstructed cache intact.
🧹 Nitpick comments (6)
sdks/node/src/dashboard/auth/oauth/config.ts (1)

73-75: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Avoid the flagged trailing-slash regex.

CodeQL flags Line 74; replacing it with deterministic trimming keeps behavior and avoids a blocking static-analysis finding.

Proposed fix
 export function callbackUrl(config: OAuthConfig, slot: string): string {
-  return `${config.redirectBaseUrl.replace(/\/+$/, "")}/api/auth/oauth/callback/${slot}`;
+  let base = config.redirectBaseUrl;
+  while (base.endsWith("/")) {
+    base = base.slice(0, -1);
+  }
+  return `${base}/api/auth/oauth/callback/${slot}`;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdks/node/src/dashboard/auth/oauth/config.ts` around lines 73 - 75, The
callbackUrl helper currently uses a trailing-slash regex on
config.redirectBaseUrl, which is triggering the static-analysis finding. Update
callbackUrl to trim trailing slashes deterministically without regex while
preserving the same URL construction, and keep the change localized to the
callbackUrl function in the OAuth config module.

Source: Linters/SAST tools

sdks/node/src/dashboard/auth/oauth/stateStore.ts (1)

100-108: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Revalidate persisted next_url before returning it.

OAuthFlow.start sanitizes nextUrl, but the persisted KV row is trusted on callback; keep the store invariant safe even if a row is malformed or written by another path.

Proposed fix
+import { isSafeRedirect } from "../../urlSafety";
+
     return {
       state: stateToken,
       nonce: row.nonce,
       codeVerifier: row.code_verifier,
       slot: row.slot,
-      nextUrl: typeof row.next_url === "string" ? row.next_url : "/",
+      nextUrl: typeof row.next_url === "string" && isSafeRedirect(row.next_url) ? row.next_url : "/",
       createdAt: row.created_at ?? 0,
       expiresAt: row.expires_at,
     };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdks/node/src/dashboard/auth/oauth/stateStore.ts` around lines 100 - 108, The
OAuth callback path in stateStore is trusting persisted next_url data too
directly. In the state retrieval logic that returns the row fields (including
nextUrl), revalidate the stored value with the same sanitization/invariant
checks used by OAuthFlow.start before exposing it, and fall back to "/" for any
malformed or unsafe value. Keep the fix localized to the stateStore read path so
the returned object always preserves the expected nextUrl invariant even if the
KV row was tampered with or written elsewhere.
sdks/node/src/cli/commands/dashboard.ts (1)

14-34: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Consider warning when --insecure-cookies is used.

Silently dropping the Secure cookie attribute is easy to misuse in a non-dev environment (e.g., a copy-pasted CLI invocation). A one-line stderr warning would reduce accidental misconfiguration risk.

♻️ Proposed addition
       const server = serveDashboard(queue, {
         port,
         host,
         auth: options.token ? { token: options.token } : undefined,
         secureCookies: options.insecureCookies ? false : undefined,
       });
+      if (options.insecureCookies) {
+        process.stderr.write("warning: --insecure-cookies disables the Secure cookie attribute; use only for plain-HTTP dev\n");
+      }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdks/node/src/cli/commands/dashboard.ts` around lines 14 - 34, The dashboard
CLI currently enables --insecure-cookies without any user-facing warning, which
can be easy to misuse. Update registerDashboard in dashboard.ts so that when
options.insecureCookies is set, it emits a one-line warning to stderr before
calling serveDashboard, using the existing action handler and the
insecureCookies flag as the trigger. Keep the warning concise and explicit that
the Secure cookie attribute is being disabled.
sdks/node/test/dashboard/sessionAuth.test.ts (1)

1-44: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Shared dashboard-test bootstrap duplicated across five test files.

The beforeAll static-asset build guard, temp-db beforeEach, and server teardown here are duplicated nearly verbatim across inspectRoutes.test.ts, opsSettings.test.ts, overridesMiddleware.test.ts, server.test.ts, and webhookDeliveries.test.ts. Consider extracting a shared startDashboardTestServer()/ensureDashboardBuilt() helper (similar in spirit to seedAdminAndSession) into a shared test-utils module to reduce copy-paste drift as the dashboard test surface keeps growing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdks/node/test/dashboard/sessionAuth.test.ts` around lines 1 - 44, The
dashboard test setup is duplicated across multiple test files, including the
static build guard, temporary queue/database initialization, server startup, and
teardown logic. Extract this shared bootstrap into a reusable test helper such
as ensureDashboardBuilt() and startDashboardTestServer() in the dashboard
testing utilities module, then update sessionAuth.test.ts and the related
dashboard tests to use it instead of repeating the same
beforeAll/beforeEach/afterEach logic.
sdks/node/src/worker.ts (1)

76-87: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Per-invocation settings read for middleware disables could add hot-path latency.

middlewareFor() performs a synchronous KV read (getSetting) on every task invocation (line 154) and again on every outcome dispatch (line 198) — two storage round-trips per job purely to check disable toggles. This is an explicit "live toggle" tradeoff per the comment, but for high-throughput queues this doubles the storage reads per job. Consider a short-TTL in-memory cache (invalidated on setDisabled/clearFor, or simply refreshed every N seconds) to bound this cost while still picking up toggles promptly.

Also applies to: 198-198

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdks/node/src/worker.ts` around lines 76 - 87, The live per-invocation
middleware disable lookup in MiddlewareDisableStore is causing extra synchronous
KV reads in middlewareFor(), which is used on both task handling and outcome
dispatch. Update MiddlewareDisableStore/getFor usage to add a short-TTL
in-memory cache or periodic refresh so repeated calls within the TTL reuse
cached disables, and make sure setDisabled and clearFor invalidate the cache so
toggle changes still propagate promptly.
sdks/node/src/proxies/metrics.ts (1)

4-12: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

durations sliding window uses push+shift(), which is O(n) per sample once at capacity.

Once durations.length reaches MAX_SAMPLES (1000), every subsequent recordReconstruction() call does a shift() that re-indexes the remaining ~1000 elements. Under sustained proxy-reconstruction throughput this becomes a repeated O(n) cost on a metrics hot path. A fixed-size ring buffer (array + write index modulo MAX_SAMPLES) would make this O(1) per sample.

♻️ Sketch of a ring-buffer alternative
 interface HandlerMetrics {
   reconstructions: number;
   errors: number;
   cleanupErrors: number;
   checksumFailures: number;
   totalDurationMs: number;
   maxDurationMs: number;
-  durations: number[];
+  durations: number[]; // fixed-length ring buffer
+  durationsWriteIndex: number;
+  durationsFilled: number;
 }
   recordReconstruction(handlerName: string, durationMs: number): void {
     const entry = this.handler(handlerName);
     entry.reconstructions += 1;
     entry.totalDurationMs += durationMs;
     entry.maxDurationMs = Math.max(entry.maxDurationMs, durationMs);
-    entry.durations.push(durationMs);
-    if (entry.durations.length > MAX_SAMPLES) {
-      entry.durations.shift();
-    }
+    entry.durations[entry.durationsWriteIndex] = durationMs;
+    entry.durationsWriteIndex = (entry.durationsWriteIndex + 1) % MAX_SAMPLES;
+    entry.durationsFilled = Math.min(entry.durationsFilled + 1, MAX_SAMPLES);
   }

Also applies to: 50-59

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdks/node/src/proxies/metrics.ts` around lines 4 - 12, The `durations`
storage in `HandlerMetrics` and `recordReconstruction()` uses `push()` plus
`shift()`, which becomes O(n) once `MAX_SAMPLES` is reached. Replace this with a
fixed-size ring buffer approach in `metrics.ts` so samples are written at a
rotating index modulo `MAX_SAMPLES`, keeping `recordReconstruction()` O(1) while
preserving the existing metrics behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/taskito-node/src/queue/admin.rs`:
- Around line 75-109: `replay_job` in `AdminQueue` is doing synchronous storage
work on the Node.js main thread, unlike the other queue admin methods that use
`spawn_blocking`. Update `replay_job` to be async and move the
read/enqueue/record_replay sequence into a blocking task so SQLite I/O does not
block the event loop, while keeping the same replay behavior and error mapping
via `to_napi_err`.

In `@crates/taskito-node/src/queue/inspect.rs`:
- Around line 168-198: The job_dag traversal is adding the same relationship
twice, so JsJobDag.edges contains duplicates. In job_dag, keep a HashSet of seen
JsDagEdge pairs (or normalized from/to keys) while walking dependencies and
dependents, and only push a JsDagEdge into edges the first time it is
encountered. Use the existing job_dag, JsDagEdge, get_dependencies, and
get_dependents flow to locate and update the edge collection logic.

In `@sdks/node/src/dashboard/auth/oauth/config.ts`:
- Around line 196-220: parseOidcSlots and parseOidcSlot currently validate
distinct slot names that can still map to the same environment prefix, so
foo-bar and foo_bar may silently share credentials. Update the OIDC slot
validation in parseOidcSlot to reject any slot whose normalized prefix collides
with another slot’s TASKITO_DASHBOARD_OAUTH_OIDC_* prefix, and add a
duplicate-prefix check in parseOidcSlots before calling parseOidcSlot so each
provider gets a unique env namespace.

In `@sdks/node/src/dashboard/auth/oauth/idToken.ts`:
- Around line 162-165: The expiry check in the id token validation flow
currently only rejects numeric expired values, so tokens with a missing or
non-numeric exp can slip through. Update the validation in the idToken.ts logic
around the claims.exp check to require a valid numeric exp before accepting the
token, and throw IdentityFetchError for any token where exp is absent or not a
number. Keep the fix localized to the existing expiry validation path so the
rest of the id_token claim checks remain unchanged.
- Around line 151-158: The audience check in idToken validation is too
permissive for multi-audience tokens. Update the logic in the idToken claims
validation path to require that when claims.aud is an array with more than one
entry, claims.azp must be present and equal options.clientId; keep the existing
single-string aud handling intact. Use the existing claims, options.clientId,
and audienceOk check in idToken.ts to locate and extend the validation before
the token is accepted.

In `@sdks/node/src/dashboard/auth/oauth/providers.ts`:
- Around line 134-153: Validate the OIDC discovery endpoints before using them
in the OAuth provider flow: in getDiscovery() and getJwks() (and the related
token exchange path in the same provider class), reject authorization_endpoint,
token_endpoint, and jwks_uri values that use plain http:// unless they point to
a local host. Add a shared endpoint validation helper or equivalent check near
these methods, and call it immediately after fetching the DiscoveryDocument and
before any request uses those URLs.

In `@sdks/node/src/dashboard/auth/store.ts`:
- Around line 240-250: The createUser flow in store.ts is not atomic because it
loads the users map, awaits hashPassword, and then saves a stale snapshot,
allowing concurrent requests to overwrite each other. Update createUser() to
re-check or lock the auth:users state after the await before writing, or
otherwise make the read-modify-write sequence atomic; if multiple dashboard
processes may share the KV store, use a KV compare-and-swap/transaction or
distributed lock around the loadUsers/saveUsers path and the username existence
check.
- Around line 265-272: The deleteUser() method in the auth store only removes
the user record and leaves any existing auth:session:* entries valid, so update
this flow to also revoke that user’s active sessions when a username is deleted.
Use the existing deleteUser() logic and the session persistence helpers in the
same store to locate and remove all sessions associated with the deleted
username before returning success. Ensure the cleanup happens only after the
user exists and is removed, and keep the boolean return behavior unchanged.

In `@sdks/node/src/dashboard/handlers/settings.ts`:
- Around line 64-65: The setting value validation currently uses value.length,
which counts UTF-16 code units instead of bytes, so multibyte strings can bypass
the intended storage cap. Update the check in the settings handler that throws
BadRequestError to measure the encoded byte size of the value before comparing
against MAX_VALUE_LENGTH, and keep the error message aligned with the byte-based
limit.

In `@sdks/node/src/dashboard/index.ts`:
- Around line 55-58: The dashboard server setup currently forwards secureCookies
in createDashboardServer even when running over plain node:http, which can cause
login cookies to be marked Secure and then dropped by browsers. Update
serveDashboard() so it does not default or forward secureCookies for the
standalone HTTP server; only pass it through when the caller explicitly provides
a value, and leave the dashboard server to infer the correct behavior from the
transport.
- Around line 50-54: The admin bootstrap in dashboard startup is currently
fire-and-forget, which can let setup traffic race before the env-provisioned
admin is created. Update the startup flow around bootstrapAdminFromEnv(queue) so
it is awaited or otherwise fully settled before the server begins listening and
accepting /api/auth/setup requests, while preserving the existing non-auth path
behavior in the dashboard index startup logic.

In `@sdks/node/src/dashboard/server.ts`:
- Around line 220-225: The matchSlot helper currently calls decodeURIComponent
directly on the sliced path, which can throw on malformed OAuth slot encodings
and turn an invalid unauthenticated URL into a server error. Update matchSlot to
safely handle decode failures by catching the decodeURIComponent exception and
returning undefined, keeping the existing prefix/slot validation intact so bad
public OAuth paths fall through as a normal non-match instead of throwing.
- Around line 435-437: The /readiness handler in server.ts always returns 200
even when readiness(queue) reports degraded; update the endpoint logic in the
request handler to inspect the readiness status and return a non-2xx response
for degraded states so Kubernetes/load balancers stop routing traffic to an
unready instance. Keep the change localized around the readiness(queue) call and
sendJson(res, ...) behavior, preserving the existing readiness payload while
adjusting the HTTP status based on the status value.

In `@sdks/node/src/webhooks/deliveryLog.ts`:
- Around line 82-84: The pagination logic in deliveryLog’s row slicing currently
passes raw `filters.offset` and `filters.limit` values into `Array.slice`, which
can produce negative-index behavior. Normalize the inputs in the
`fromRow`/pagination path by clamping `offset` and `limit` to non-negative
values before slicing, so `rows.slice(...)` always returns the expected page for
dashboard/API callers.

In `@sdks/node/src/webhooks/manager.ts`:
- Around line 118-120: The webhook delivery history is split because
deliveries() reads from DeliveryLog while WebhooksManager.test() still sends via
Deliverer without persisting anything, so dashboard test deliveries never appear
in the audit surface. Update WebhooksManager.test() to record test deliveries in
the same persisted log path used by deliveries()/DeliveryLog, and make sure any
success/failure metadata is captured consistently with normal deliveries so test
runs are included in delivery history.
- Around line 175-177: The detached promise chain in Manager’s webhook dispatch
can produce unhandled rejections when deliver() or deliveryLog.record() fails.
Update the async fire-and-forget path in the deliverer/deliveryLog.record flow
to catch and handle those failures explicitly, using the existing webhook
dispatch logic in Manager so unexpected errors are routed through the emitter’s
rejection handling instead of escaping.

In `@sdks/node/src/worker.ts`:
- Around line 154-165: `middlewareFor(invocation.taskName)` is evaluated before
the cleanup-owning try/finally in `worker.ts`, so a throw there can skip poller
cancellation and `scope.teardown()`. Move the `middlewareFor()` lookup into the
`try` path inside the worker invocation flow, and initialize `chain` to a safe
default so the `catch`/cleanup logic still has a defined value. Keep the fix
centered around `middlewareFor`, `runWithResolver`, and the existing `finally`
cleanup that clears `poller` and tears down `scope`.

---

Outside diff comments:
In `@sdks/node/src/proxies/proxy-session.ts`:
- Around line 71-76: ProxySession.reconstruct currently calls
this.proxies.verifyRef directly, so signature/expiry/purpose failures are not
recorded in the session reconstruction metrics like they are through
Proxies.reconstruct. Update ProxySession.reconstruct to route verification
through the same metrics-tracking path, or explicitly record verification
failures around verifyRef using the existing reconstruction metrics helpers.
Keep the behavior for successful reconstruction and the reconstructed cache
intact.

---

Nitpick comments:
In `@sdks/node/src/cli/commands/dashboard.ts`:
- Around line 14-34: The dashboard CLI currently enables --insecure-cookies
without any user-facing warning, which can be easy to misuse. Update
registerDashboard in dashboard.ts so that when options.insecureCookies is set,
it emits a one-line warning to stderr before calling serveDashboard, using the
existing action handler and the insecureCookies flag as the trigger. Keep the
warning concise and explicit that the Secure cookie attribute is being disabled.

In `@sdks/node/src/dashboard/auth/oauth/config.ts`:
- Around line 73-75: The callbackUrl helper currently uses a trailing-slash
regex on config.redirectBaseUrl, which is triggering the static-analysis
finding. Update callbackUrl to trim trailing slashes deterministically without
regex while preserving the same URL construction, and keep the change localized
to the callbackUrl function in the OAuth config module.

In `@sdks/node/src/dashboard/auth/oauth/stateStore.ts`:
- Around line 100-108: The OAuth callback path in stateStore is trusting
persisted next_url data too directly. In the state retrieval logic that returns
the row fields (including nextUrl), revalidate the stored value with the same
sanitization/invariant checks used by OAuthFlow.start before exposing it, and
fall back to "/" for any malformed or unsafe value. Keep the fix localized to
the stateStore read path so the returned object always preserves the expected
nextUrl invariant even if the KV row was tampered with or written elsewhere.

In `@sdks/node/src/proxies/metrics.ts`:
- Around line 4-12: The `durations` storage in `HandlerMetrics` and
`recordReconstruction()` uses `push()` plus `shift()`, which becomes O(n) once
`MAX_SAMPLES` is reached. Replace this with a fixed-size ring buffer approach in
`metrics.ts` so samples are written at a rotating index modulo `MAX_SAMPLES`,
keeping `recordReconstruction()` O(1) while preserving the existing metrics
behavior.

In `@sdks/node/src/worker.ts`:
- Around line 76-87: The live per-invocation middleware disable lookup in
MiddlewareDisableStore is causing extra synchronous KV reads in middlewareFor(),
which is used on both task handling and outcome dispatch. Update
MiddlewareDisableStore/getFor usage to add a short-TTL in-memory cache or
periodic refresh so repeated calls within the TTL reuse cached disables, and
make sure setDisabled and clearFor invalidate the cache so toggle changes still
propagate promptly.

In `@sdks/node/test/dashboard/sessionAuth.test.ts`:
- Around line 1-44: The dashboard test setup is duplicated across multiple test
files, including the static build guard, temporary queue/database
initialization, server startup, and teardown logic. Extract this shared
bootstrap into a reusable test helper such as ensureDashboardBuilt() and
startDashboardTestServer() in the dashboard testing utilities module, then
update sessionAuth.test.ts and the related dashboard tests to use it instead of
repeating the same beforeAll/beforeEach/afterEach logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 90a99deb-67ac-4775-9a9f-8c419f61512a

📥 Commits

Reviewing files that changed from the base of the PR and between 3e15ab6 and a9b970d.

📒 Files selected for processing (62)
  • crates/taskito-node/src/convert/mod.rs
  • crates/taskito-node/src/convert/ops.rs
  • crates/taskito-node/src/queue/admin.rs
  • crates/taskito-node/src/queue/inspect.rs
  • sdks/node/src/cli/commands/dashboard.ts
  • sdks/node/src/contrib/express.ts
  • sdks/node/src/contrib/fastify.ts
  • sdks/node/src/dashboard/api.ts
  • sdks/node/src/dashboard/auth/context.ts
  • sdks/node/src/dashboard/auth/handlers.ts
  • sdks/node/src/dashboard/auth/index.ts
  • sdks/node/src/dashboard/auth/oauth/config.ts
  • sdks/node/src/dashboard/auth/oauth/flow.ts
  • sdks/node/src/dashboard/auth/oauth/idToken.ts
  • sdks/node/src/dashboard/auth/oauth/identity.ts
  • sdks/node/src/dashboard/auth/oauth/index.ts
  • sdks/node/src/dashboard/auth/oauth/pkce.ts
  • sdks/node/src/dashboard/auth/oauth/providers.ts
  • sdks/node/src/dashboard/auth/oauth/stateStore.ts
  • sdks/node/src/dashboard/auth/store.ts
  • sdks/node/src/dashboard/auth/tokenAuth.ts
  • sdks/node/src/dashboard/errors.ts
  • sdks/node/src/dashboard/handlers/contract.ts
  • sdks/node/src/dashboard/handlers/core.ts
  • sdks/node/src/dashboard/handlers/index.ts
  • sdks/node/src/dashboard/handlers/metrics.ts
  • sdks/node/src/dashboard/handlers/middleware.ts
  • sdks/node/src/dashboard/handlers/ops.ts
  • sdks/node/src/dashboard/handlers/overrides.ts
  • sdks/node/src/dashboard/handlers/settings.ts
  • sdks/node/src/dashboard/index.ts
  • sdks/node/src/dashboard/policy.ts
  • sdks/node/src/dashboard/routes.ts
  • sdks/node/src/dashboard/server.ts
  • sdks/node/src/dashboard/stores/index.ts
  • sdks/node/src/dashboard/stores/middlewareDisables.ts
  • sdks/node/src/dashboard/stores/overrides.ts
  • sdks/node/src/dashboard/testing.ts
  • sdks/node/src/dashboard/urlSafety.ts
  • sdks/node/src/index.ts
  • sdks/node/src/interception.ts
  • sdks/node/src/middleware.ts
  • sdks/node/src/native.ts
  • sdks/node/src/proxies/index.ts
  • sdks/node/src/proxies/metrics.ts
  • sdks/node/src/proxies/proxies.ts
  • sdks/node/src/proxies/proxy-session.ts
  • sdks/node/src/queue.ts
  • sdks/node/src/types.ts
  • sdks/node/src/webhooks/deliveryLog.ts
  • sdks/node/src/webhooks/manager.ts
  • sdks/node/src/worker.ts
  • sdks/node/test/dashboard/inspectRoutes.test.ts
  • sdks/node/test/dashboard/oauth.test.ts
  • sdks/node/test/dashboard/oauthEndpoints.test.ts
  • sdks/node/test/dashboard/opsSettings.test.ts
  • sdks/node/test/dashboard/overridesMiddleware.test.ts
  • sdks/node/test/dashboard/server.test.ts
  • sdks/node/test/dashboard/sessionAuth.test.ts
  • sdks/node/test/dashboard/webhookDeliveries.test.ts
  • sdks/node/test/integrations/express.test.ts
  • sdks/node/test/integrations/fastify.test.ts
💤 Files with no reviewable changes (1)
  • sdks/node/src/dashboard/api.ts

Comment thread crates/taskito-node/src/queue/admin.rs
Comment thread crates/taskito-node/src/queue/inspect.rs
Comment thread sdks/node/src/dashboard/auth/oauth/config.ts Outdated
Comment thread sdks/node/src/dashboard/auth/oauth/idToken.ts
Comment thread sdks/node/src/dashboard/auth/oauth/idToken.ts
Comment thread sdks/node/src/dashboard/server.ts
Comment thread sdks/node/src/webhooks/deliveryLog.ts Outdated
Comment thread sdks/node/src/webhooks/manager.ts
Comment thread sdks/node/src/webhooks/manager.ts Outdated
Comment thread sdks/node/src/worker.ts Outdated
First-run setup, password login with PBKDF2 sessions, CSRF
double-submit, and admin/viewer roles, persisted via new Queue
settings-KV accessors under the cross-SDK auth key layout.
Shared-token mode stays as a legacy option.
Adds /api/settings CRUD (protected namespaces hidden), /health +
/readiness + Prometheus /metrics with optional bearer gate, the
KEDA scaler payload, heartbeat-aggregated /api/resources, job
errors/logs routes, and dead-letter purge.
Delivery history moves from an in-memory ring to the settings KV
under the cross-SDK row layout, surviving restarts. Adds delivery
detail, delivery replay, and rotate-secret endpoints.
Operators tune rate limits, concurrency, retries, and pause state
from the dashboard; overrides persist in the settings KV under the
cross-SDK key layout and apply at worker startup. Per-task
middleware disables are re-read each invocation, so toggles take
effect without a restart.
Group files into auth/, handlers/, and stores/ with index barrels,
split route policy out of the route table, replace error helper
functions with a typed exception hierarchy (DashboardError
subclasses + store-level ValidationError), and drop the unused
api.ts module.
New napi bindings (queryTaskLogs, listCircuitBreakers, replayJob,
getReplayHistory, jobDag) surfaced as Queue methods and dashboard
routes, closing the remaining read-API gaps.
Google, GitHub, and generic OIDC providers with PKCE S256, full
id_token validation on node:crypto, a single-use KV-backed state
store, domain/org allowlists, and admin-email role mapping —
configured via the cross-SDK TASKITO_DASHBOARD_OAUTH_* env vars.
No new runtime dependencies.
Per-handler proxy reconstruction metrics and enqueue-interception
counters, surfaced as queue accessors plus /api/proxy-stats and
/api/interception-stats.
Hash before the load-check-save so concurrent creates can't clobber
each other across the PBKDF2 await, and revoke live sessions when a
user is deleted.
@pratyush618 pratyush618 force-pushed the feat/node-dashboard-parity branch from f475897 to 84d2e7b Compare July 7, 2026 10:12
@pratyush618 pratyush618 merged commit 78a5078 into master Jul 7, 2026
35 checks passed
@pratyush618 pratyush618 deleted the feat/node-dashboard-parity branch July 7, 2026 10:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants