Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions packages/cli/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env node

import { execSync, spawn } from 'child_process'
import { randomBytes } from 'crypto'
import { existsSync, mkdirSync } from 'fs'
import { homedir } from 'os'
import { join } from 'path'
Expand All @@ -15,6 +16,10 @@ const REALTIME_CONTAINER = 'simstudio-realtime'
const APP_CONTAINER = 'simstudio-app'
const DEFAULT_PORT = '3000'

function generateHexSecret(bytes = 32): string {
return randomBytes(bytes).toString('hex')
}

const program = new Command()

program.name('simstudio').description('Run Sim using Docker').version('0.1.0')
Expand Down Expand Up @@ -84,6 +89,10 @@ async function cleanupExistingContainers(): Promise<void> {

async function main() {
const options = program.parse().opts()
const betterAuthSecret = generateHexSecret()
const encryptionKey = generateHexSecret()
const internalApiSecret = generateHexSecret()
const apiEncryptionKey = generateHexSecret()
Comment on lines +92 to +95
Copy link
Contributor

Choose a reason for hiding this comment

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

P0 Secrets regenerated on every restart — encrypted data becomes unreadable

New secrets are generated on every invocation of npx simstudio. Because cleanupExistingContainers() stops and removes the old containers but the PostgreSQL data volume at ~/.simstudio/data/postgres is persisted on disk, the next startup uses a different ENCRYPTION_KEY and API_ENCRYPTION_KEY against a database that already has data encrypted under the previous keys. This will silently make any previously-encrypted credential/secret rows unreadable.

Similarly, BETTER_AUTH_SECRET changing on every restart will invalidate all existing user sessions, forcing re-authentication every time the CLI is re-run.

The fix is to persist the generated secrets to disk the first time they are created and reload them on subsequent runs. A simple approach:

// Persist/load secrets from ~/.simstudio/secrets.json
// On first run: generate all four values and write the file with mode 0o600
// On subsequent runs: read the file and reuse the same values

Key requirements:

  • The secrets file should only be created once (no regeneration if the file already exists)
  • The file permissions should be restricted to owner-only (0o600) to avoid leaking secrets to other users on the same machine
  • The ~/.simstudio/ directory already exists at this point in the flow (the dataDir mkdir runs earlier), so writing there is safe

Copy link

Choose a reason for hiding this comment

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

Secrets regenerated on restart, corrupting persisted encrypted data

High Severity

ENCRYPTION_KEY, API_ENCRYPTION_KEY, BETTER_AUTH_SECRET, and INTERNAL_API_SECRET are freshly generated on every CLI invocation, but the PostgreSQL database is persisted across runs via a host volume at ~/.simstudio/data. On a second boot, the new random secrets won't match the ones used to encrypt existing data, making all previously encrypted values (credentials, API keys, auth sessions) unreadable. The secrets need to be persisted to disk (e.g., alongside the data directory) and reused on subsequent runs.

Additional Locations (1)
Fix in Cursor Fix in Web


console.log(chalk.blue('🚀 Starting Sim...'))

Expand Down Expand Up @@ -215,7 +224,9 @@ async function main() {
'-e',
`NEXT_PUBLIC_APP_URL=http://localhost:${port}`,
'-e',
'BETTER_AUTH_SECRET=your_auth_secret_here',
`BETTER_AUTH_SECRET=${betterAuthSecret}`,
'-e',
`INTERNAL_API_SECRET=${internalApiSecret}`,
'ghcr.io/simstudioai/realtime:latest',
])

Expand Down Expand Up @@ -243,9 +254,13 @@ async function main() {
'-e',
`NEXT_PUBLIC_APP_URL=http://localhost:${port}`,
'-e',
'BETTER_AUTH_SECRET=your_auth_secret_here',
`BETTER_AUTH_SECRET=${betterAuthSecret}`,
'-e',
`ENCRYPTION_KEY=${encryptionKey}`,
'-e',
`INTERNAL_API_SECRET=${internalApiSecret}`,
'-e',
'ENCRYPTION_KEY=your_encryption_key_here',
`API_ENCRYPTION_KEY=${apiEncryptionKey}`,
'ghcr.io/simstudioai/simstudio:latest',
])

Expand Down