Skip to content

refactor(streams): replace STREAMS_SECRET with session-based auth#1360

Merged
Kitenite merged 1 commit into
mainfrom
kitenite/replace-streams-secret
Feb 10, 2026
Merged

refactor(streams): replace STREAMS_SECRET with session-based auth#1360
Kitenite merged 1 commit into
mainfrom
kitenite/replace-streams-secret

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented Feb 10, 2026

Summary

  • Replace shared static STREAMS_SECRET with per-user session token validation via direct DB query against auth.sessions table
  • Desktop now reads the user's encrypted session token from disk (loadToken()) instead of using a static env var
  • Removes STREAMS_SECRET from all source code, CI/CD pipelines, and setup scripts

Changes

Streams server (apps/streams)

  • src/server.ts: Replace string-comparison middleware with Drizzle query — validates Bearer token against auth.sessions (checks token + expiry), attaches userId to Hono context
  • src/env.ts: Replace STREAMS_SECRET with DATABASE_URL
  • src/index.ts: Remove authToken from createServer() call
  • package.json: Add @superset/db and drizzle-orm dependencies
  • Dockerfile: Include packages/db in build and runtime stages

Desktop (apps/desktop)

  • session-manager.ts: Make buildProxyHeaders() async using loadToken() instead of static secret; await at all 5 call sites
  • ai-chat/index.ts: Make getConfig async, return session token from loadToken()
  • env.main.ts: Remove STREAMS_SECRET from schema and runtimeEnv

CI/CD & config cleanup

  • turbo.jsonc: Remove STREAMS_SECRET from globalEnv
  • ci.yml: Remove STREAMS_SECRET env var and TODO comment
  • deploy-preview.yml: Replace STREAMS_SECRET with DATABASE_URL; add needs: deploy-database with artifact download
  • deploy-production.yml: Replace STREAMS_SECRET with DATABASE_URL in flyctl secrets set
  • .superset/setup.sh: Remove step_setup_streams() and STREAMS_SECRET env output

Test Plan

  • grep -r STREAMS_SECRET returns only docs files (no source code matches)
  • bun turbo run typecheck --filter=@superset/streams passes
  • bun turbo run typecheck --filter=@superset/desktop passes
  • Desktop sign-in → streams requests use session token → server validates via DB → returns 200
  • Unauthenticated/expired token requests to streams return 401
  • SSE connections in renderer work with session token

Summary by CodeRabbit

Release Notes

  • Refactor

    • Authentication system redesigned from environment-based shared secrets to session-token validation via database lookups.
    • Desktop and server components updated to use encrypted user tokens for enhanced security and dynamic credential verification.
  • Chores

    • Updated infrastructure, dependencies, and CI/CD workflows to support the new session-based authentication architecture.

Replace the shared static STREAMS_SECRET with per-user session token
validation. The streams server now queries the auth.sessions table
directly via @superset/db to validate Bearer tokens, providing
per-user identity, automatic expiration, and revocation support.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 10, 2026

📝 Walkthrough

Walkthrough

This PR replaces a shared STREAMS_SECRET authentication model with a database-backed session token system. It updates CI/CD workflows to use DATABASE_URL, removes static secret setup from build configuration, and converts the Streams server to validate tokens against a database table instead of checking a pre-configured secret. The desktop client now dynamically loads encrypted tokens at runtime.

Changes

Cohort / File(s) Summary
CI/CD Workflows
.github/workflows/ci.yml, .github/workflows/deploy-preview.yml, .github/workflows/deploy-production.yml, turbo.jsonc
Removed STREAMS_SECRET references; deploy-preview.yml now depends on deploy-database job and loads DATABASE_URL from artifact; production deployment updated to set DATABASE_URL instead of STREAMS_SECRET.
Build & Setup Configuration
.superset/setup.sh, apps/streams/Dockerfile, apps/streams/package.json
Removed STREAMS_SECRET generation from setup script; Dockerfile now includes packages/db directory for schema support; added @superset/db and drizzle-orm dependencies to Streams package.
Environment Configuration
apps/desktop/src/main/env.main.ts, apps/streams/src/env.ts
Removed STREAMS_SECRET from both desktop and Streams env schemas; Streams env now includes DATABASE_URL field for token validation.
Streams Server Authentication
apps/streams/src/server.ts, apps/streams/src/index.ts
Replaced static authToken option with database-backed middleware that validates Bearer tokens against auth.sessions table; added SessionEnv type for request context; removed authToken initialization.
Desktop Client Token Handling
apps/desktop/src/lib/trpc/routers/ai-chat/index.ts, apps/desktop/src/lib/trpc/routers/ai-chat/utils/session-manager/session-manager.ts
Made getConfig and buildProxyHeaders asynchronous; both now use loadToken() to retrieve user token at runtime instead of accessing static STREAMS_SECRET.
Documentation
docs/replace-streams-secret-with-session-auth.md
Comprehensive guide documenting migration from static secret to session token architecture, including server, client, and infrastructure changes.

Sequence Diagram(s)

sequenceDiagram
    participant Desktop as Desktop Client
    participant Token as Token Storage<br/>(encrypted file)
    participant Server as Streams Server
    participant DB as Database<br/>(auth.sessions)

    Desktop->>Token: loadToken()
    Token-->>Desktop: encrypted token
    Desktop->>Server: /v1/* request<br/>+ Bearer token
    Server->>DB: SELECT * FROM sessions<br/>WHERE token = ?
    DB-->>Server: {token, userId, expires}
    alt Token valid & not expired
        Server->>Server: Attach userId to context
        Server-->>Desktop: 200 + response data
    else Token invalid/expired
        Server-->>Desktop: 401 Unauthorized
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 No secrets whispered in the wind so clear,
From databases the truth we now hold dear,
Each session token signed with time's own hand,
The streams now flow through security's command,
A rabbit hops through tokens freshly minted! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main refactoring: replacing a static STREAMS_SECRET with session-based authentication, which aligns with the comprehensive changes across the codebase.
Description check ✅ Passed The PR description is comprehensive and well-structured, covering all major changes, affected components, and includes a test plan with completed and pending tests. All required template sections are present and filled out.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch kitenite/replace-streams-secret

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

❤️ Share

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @.github/workflows/deploy-preview.yml:
- Around line 137-140: The step named "Load database URL" is leaving variable
expansions unquoted; update the echo to quote the DATABASE_URL expansion and the
GITHUB_ENV target to prevent word splitting/globbing: change the echo line so it
uses the $DATABASE_URL variable inside double quotes (e.g. echo
"DATABASE_URL=$DATABASE_URL" with the expansion quoted) and quote $GITHUB_ENV in
the redirection (>> "$GITHUB_ENV"); reference the step name "Load database URL"
and the variables $DATABASE_URL and $GITHUB_ENV when making the change.

In `@apps/streams/src/server.ts`:
- Around line 1-3: Remove the redundant dotenv initialization in client.ts:
delete the config({ path: ".env", quiet: true }) call in
packages/db/src/client.ts (the one before importing env.ts), since env.ts
already calls config(...) with the absolute monorepo path; keep the import of
env.ts and the db singleton logic intact so DATABASE_URL resolution continues to
come from env.ts.
🧹 Nitpick comments (3)
apps/desktop/src/lib/trpc/routers/ai-chat/utils/session-manager/session-manager.ts (1)

33-42: Stale token risk in long-running agent sessions.

buildProxyHeaders() is called once in startAgent (Line 265), and the resulting headers object is reused for all subsequent onChunk POSTs and the finally cleanup requests. If the session token is rotated or invalidated mid-execution, those downstream requests will silently fail with 401.

Given the 30-day expiry this is unlikely in practice, but if token rotation is ever shortened (or if the user signs out mid-run), this could cause hard-to-diagnose failures. Consider re-fetching headers for the finally block at minimum, since that's the critical path for marking the message as complete.

docs/replace-streams-secret-with-session-auth.md (1)

27-27: Add a language identifier to the fenced code block to satisfy markdownlint (MD040).

Since this is a text diagram, use ```text to silence the warning.

apps/streams/src/server.ts (1)

67-86: DB query on every request — consider caching for high-frequency endpoints.

The onChunk callback in the desktop session manager fires a POST to /v1/sessions/:id/chunks for every streaming chunk, and each of those hits this middleware, triggering a DB round-trip. Under heavy streaming this could add meaningful latency and DB load.

Consider a short-lived in-memory cache (e.g., LRU with 30–60s TTL) keyed by token → userId. The 30-day session expiry and the fact that revocation is not instantaneous anyway make a brief cache safe.

Comment on lines +137 to +140
- name: Load database URL
run: |
source database-status.env
echo "DATABASE_URL=$DATABASE_URL" >> $GITHUB_ENV
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Quote the variable to prevent word splitting.

Per the static analysis hint (SC2086), $DATABASE_URL should be double-quoted to prevent globbing and word splitting, especially since connection strings can contain special characters.

Proposed fix
       - name: Load database URL
         run: |
           source database-status.env
-          echo "DATABASE_URL=$DATABASE_URL" >> $GITHUB_ENV
+          echo "DATABASE_URL=${DATABASE_URL}" >> "$GITHUB_ENV"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Load database URL
run: |
source database-status.env
echo "DATABASE_URL=$DATABASE_URL" >> $GITHUB_ENV
- name: Load database URL
run: |
source database-status.env
echo "DATABASE_URL=${DATABASE_URL}" >> "$GITHUB_ENV"
🧰 Tools
🪛 actionlint (1.7.10)

[error] 138-138: shellcheck reported issue in this script: SC2086:info:2:38: Double quote to prevent globbing and word splitting

(shellcheck)

🤖 Prompt for AI Agents
In @.github/workflows/deploy-preview.yml around lines 137 - 140, The step named
"Load database URL" is leaving variable expansions unquoted; update the echo to
quote the DATABASE_URL expansion and the GITHUB_ENV target to prevent word
splitting/globbing: change the echo line so it uses the $DATABASE_URL variable
inside double quotes (e.g. echo "DATABASE_URL=$DATABASE_URL" with the expansion
quoted) and quote $GITHUB_ENV in the redirection (>> "$GITHUB_ENV"); reference
the step name "Load database URL" and the variables $DATABASE_URL and
$GITHUB_ENV when making the change.

Comment on lines +1 to +3
import { db } from "@superset/db";
import { sessions } from "@superset/db/schema/auth";
import { and, eq, gt } from "drizzle-orm";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Locate packages/db directory structure
cd apps/streams || cd . 2>/dev/null
find . -path "*/packages/db*" -type d 2>/dev/null | head -5

Repository: superset-sh/superset

Length of output: 46


🏁 Script executed:

#!/bin/bash
# Search for database client initialization in packages/db
fd -t f '\.(ts|js)$' packages/db --exec head -50 {} \; 2>/dev/null | head -100

Repository: superset-sh/superset

Length of output: 2334


🏁 Script executed:

#!/bin/bash
# Look for DATABASE_URL references in packages/db
rg 'DATABASE_URL' packages/db 2>/dev/null || echo "No matches found"

Repository: superset-sh/superset

Length of output: 415


🏁 Script executed:

#!/bin/bash
# Check the structure of packages/db
ls -la packages/db/ 2>/dev/null || echo "Directory not found at packages/db"

Repository: superset-sh/superset

Length of output: 484


🏁 Script executed:

#!/bin/bash
# Get the full content of env.ts in packages/db
cat packages/db/src/env.ts

Repository: superset-sh/superset

Length of output: 554


🏁 Script executed:

#!/bin/bash
# Check if there's any dotenv config or initialization in packages/db
rg 'config|dotenv' packages/db/src/ -A 2 -B 2

Repository: superset-sh/superset

Length of output: 1423


Remove redundant dotenv config from client.ts; DATABASE_URL resolution is already handled correctly by env.ts.

The db singleton correctly reads DATABASE_URL from the streams server's environment because packages/db/src/env.ts uses an absolute path to load .env from the monorepo root: config({ path: path.resolve(__dirname, "../../../.env"), quiet: true }). However, packages/db/src/client.ts has a redundant config({ path: ".env", quiet: true }) with a relative path that is unnecessary and creates confusion. Remove this line since env.ts is imported immediately after and handles the environment setup correctly.

🤖 Prompt for AI Agents
In `@apps/streams/src/server.ts` around lines 1 - 3, Remove the redundant dotenv
initialization in client.ts: delete the config({ path: ".env", quiet: true })
call in packages/db/src/client.ts (the one before importing env.ts), since
env.ts already calls config(...) with the absolute monorepo path; keep the
import of env.ts and the db singleton logic intact so DATABASE_URL resolution
continues to come from env.ts.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 10, 2026

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch
  • ✅ Electric Fly.io app
  • ✅ Streams Fly.io app

Thank you for your contribution! 🎉

@Kitenite Kitenite merged commit 090f933 into main Feb 10, 2026
14 of 15 checks passed
@Kitenite Kitenite deleted the kitenite/replace-streams-secret branch February 10, 2026 09:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant