Compare commits

..

12 Commits

Author SHA1 Message Date
Vikhyath Mondreti
1b7437af14 v0.4.21: more internal auth changes, supabase vector search tool 2025-10-21 18:49:09 -07:00
Waleed
4d7ebd8bcb feat(supabase): added vector search tool and updated docs (#1707)
* feat(supabase): added vector search tool and updated docs

* exclude generic webhook from docs gen

* change items to pages in meta.json for tools directory in the docs
2025-10-21 18:48:18 -07:00
Vikhyath Mondreti
ca1156a6c2 fix(base-url): use getBaseUrl helper in copilot revert state endpoint (#1706)
* revert base url

* fix tests"
;
2025-10-21 15:44:05 -10:00
Vikhyath Mondreti
89eb1849d0 fix(external-route): handleInternalRequest should still check if request internal 2025-10-21 15:00:33 -10:00
Vikhyath Mondreti
1d4833f485 fix case sensitive header (#1705) 2025-10-21 14:51:13 -10:00
Waleed
d5902e91da fix(kb): added internal auth for mistral OCR tool via KB (#1704) 2025-10-21 16:56:53 -07:00
Vikhyath Mondreti
9751c9f5c4 v0.4.20: internal request, kb url fixes, docs styling 2025-10-21 13:00:00 -07:00
Vikhyath Mondreti
e6ba323de4 Merge pull request #1700 from simstudioai/fix/internal-req
fix(internal-req): add internal token to server side
2025-10-21 12:07:54 -07:00
Waleed
859711991f fix(docs): made sidebar directories scrollable, stylistic changes (#1702) 2025-10-21 12:04:19 -07:00
Waleed
c178a90f02 improvement(helm): added additional envvars to helm charts (#1695)
* improvement(helm): added additional envvars to helm charts

* updated docs
2025-10-21 12:02:51 -07:00
Waleed
eb8995ee7c fix(kb): add base URL for kb fetches (#1701) 2025-10-21 12:02:18 -07:00
Vikhyath Mondreti
b269447539 fix(internal-req): add internal token to server side 2025-10-21 08:36:15 -10:00
34 changed files with 410 additions and 20 deletions

View File

@@ -97,7 +97,7 @@ export function SidebarFolder({
<div
className={cn(
'overflow-hidden transition-all duration-200 ease-in-out',
open ? 'max-h-[2000px] opacity-100' : 'max-h-0 opacity-0'
open ? 'max-h-[10000px] opacity-100' : 'max-h-0 opacity-0'
)}
>
<ul className='mt-0.5 ml-2 space-y-[0.0625rem] border-gray-200/60 border-l pl-2.5 dark:border-gray-700/60'>

View File

@@ -39,7 +39,7 @@ export function Navbar() {
</div>
{/* Center cluster: search */}
<div className='flex flex-1 items-center justify-center'>
<div className='flex flex-1 items-center justify-center pl-32'>
<SearchTrigger />
</div>

View File

@@ -15,7 +15,7 @@ export function SearchTrigger() {
return (
<button
type='button'
className='flex h-10 w-[500px] items-center gap-2 rounded-xl border border-border/50 px-3 py-2 text-sm backdrop-blur-xl transition-colors hover:border-border'
className='flex h-10 w-[460px] items-center gap-2 rounded-xl border border-border/50 px-3 py-2 text-sm backdrop-blur-xl transition-colors hover:border-border'
style={{
backgroundColor: 'hsla(0, 0%, 5%, 0.85)',
backdropFilter: 'blur(33px) saturate(180%)',

View File

@@ -75,6 +75,7 @@ Send a message to a Discord channel
| `channelId` | string | Yes | The Discord channel ID to send the message to |
| `content` | string | No | The text content of the message |
| `serverId` | string | Yes | The Discord server ID \(guild ID\) |
| `files` | file[] | No | Files to attach to the message |
#### Output

View File

@@ -230,4 +230,4 @@ curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
- Category: `triggers`
- Type: `generic_webhook`
- **File Support**: Available via input format configuration
- **Max File Size**: 20MB per file
- **Max File Size**: 20MB per file

View File

@@ -88,8 +88,9 @@ Upload a file to Google Drive
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `fileName` | string | Yes | The name of the file to upload |
| `content` | string | Yes | The content of the file to upload |
| `mimeType` | string | No | The MIME type of the file to upload |
| `file` | file | No | Binary file to upload \(UserFile object\) |
| `content` | string | No | Text content to upload \(use this OR file, not both\) |
| `mimeType` | string | No | The MIME type of the file to upload \(auto-detected from file if not provided\) |
| `folderSelector` | string | No | Select the folder to upload the file to |
| `folderId` | string | No | The ID of the folder to upload the file to \(internal use\) |

View File

@@ -138,6 +138,7 @@ Write or update content in a Microsoft Teams chat
| --------- | ---- | -------- | ----------- |
| `chatId` | string | Yes | The ID of the chat to write to |
| `content` | string | Yes | The content to write to the message |
| `files` | file[] | No | Files to attach to the message |
#### Output
@@ -187,6 +188,7 @@ Write or send a message to a Microsoft Teams channel
| `teamId` | string | Yes | The ID of the team to write to |
| `channelId` | string | Yes | The ID of the channel to write to |
| `content` | string | Yes | The content to write to the channel |
| `files` | file[] | No | Files to attach to the message |
#### Output

View File

@@ -66,7 +66,8 @@ Upload a file to OneDrive
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `fileName` | string | Yes | The name of the file to upload |
| `content` | string | Yes | The content of the file to upload |
| `file` | file | No | The file to upload \(binary\) |
| `content` | string | No | The text content to upload \(if no file is provided\) |
| `folderSelector` | string | No | Select the folder to upload the file to |
| `manualFolderId` | string | No | Manually entered folder ID \(advanced mode\) |

View File

@@ -161,6 +161,7 @@ Send emails using Outlook
| `conversationId` | string | No | Conversation ID for threading |
| `cc` | string | No | CC recipients \(comma-separated\) |
| `bcc` | string | No | BCC recipients \(comma-separated\) |
| `attachments` | file[] | No | Files to attach to the email |
#### Output
@@ -184,6 +185,7 @@ Draft emails using Outlook
| `body` | string | Yes | Email body content |
| `cc` | string | No | CC recipients \(comma-separated\) |
| `bcc` | string | No | BCC recipients \(comma-separated\) |
| `attachments` | file[] | No | Files to attach to the email draft |
#### Output

View File

@@ -202,6 +202,26 @@ Add a new item to a SharePoint list
| --------- | ---- | ----------- |
| `item` | object | Created SharePoint list item |
### `sharepoint_upload_file`
Upload files to a SharePoint document library
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `siteId` | string | No | The ID of the SharePoint site |
| `driveId` | string | No | The ID of the document library \(drive\). If not provided, uses default drive. |
| `folderPath` | string | No | Optional folder path within the document library \(e.g., /Documents/Subfolder\) |
| `fileName` | string | No | Optional: override the uploaded file name |
| `files` | file[] | No | Files to upload to SharePoint |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `uploadedFiles` | array | Array of uploaded file objects |
## Notes

View File

@@ -82,6 +82,7 @@ Send messages to Slack channels or users through the Slack API. Supports Slack m
| `botToken` | string | No | Bot token for Custom Bot |
| `channel` | string | Yes | Target Slack channel \(e.g., #general\) |
| `text` | string | Yes | Message text to send \(supports Slack mrkdwn formatting\) |
| `files` | file[] | No | Files to attach to the message |
#### Output

View File

@@ -205,6 +205,28 @@ Insert or update data in a Supabase table (upsert operation)
| `message` | string | Operation status message |
| `results` | array | Array of upserted records |
### `supabase_vector_search`
Perform similarity search using pgvector in a Supabase table
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `projectId` | string | Yes | Your Supabase project ID \(e.g., jdrkgepadsdopsntdlom\) |
| `functionName` | string | Yes | The name of the PostgreSQL function that performs vector search \(e.g., match_documents\) |
| `queryEmbedding` | array | Yes | The query vector/embedding to search for similar items |
| `matchThreshold` | number | No | Minimum similarity threshold \(0-1\), typically 0.7-0.9 |
| `matchCount` | number | No | Maximum number of results to return \(default: 10\) |
| `apiKey` | string | Yes | Your Supabase service role secret key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `message` | string | Operation status message |
| `results` | array | Array of records with similarity scores from the vector search. Each record includes a similarity field \(0-1\) indicating how similar it is to the query vector. |
## Notes

View File

@@ -191,6 +191,26 @@ Send animations (GIFs) to Telegram channels or users through the Telegram Bot AP
| `message` | string | Success or error message |
| `data` | object | Telegram message data including optional media |
### `telegram_send_document`
Send documents (PDF, ZIP, DOC, etc.) to Telegram channels or users through the Telegram Bot API.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `botToken` | string | Yes | Your Telegram Bot API Token |
| `chatId` | string | Yes | Target Telegram chat ID |
| `files` | file[] | No | Document file to send \(PDF, ZIP, DOC, etc.\). Max size: 50MB |
| `caption` | string | No | Document caption \(optional\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `message` | string | Success or error message |
| `data` | object | Telegram message data including document |
## Notes

View File

@@ -62,7 +62,8 @@ Process and analyze images using advanced vision models. Capable of understandin
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | API key for the selected model provider |
| `imageUrl` | string | Yes | Publicly accessible image URL |
| `imageUrl` | string | No | Publicly accessible image URL |
| `imageFile` | file | No | Image file to analyze |
| `model` | string | No | Vision model to use \(gpt-4o, claude-3-opus-20240229, etc\) |
| `prompt` | string | No | Custom prompt for image analysis |

View File

@@ -23,6 +23,13 @@ describe('Copilot Checkpoints Revert API Route', () => {
setupCommonApiMocks()
mockCryptoUuid()
// Mock getBaseUrl to return localhost for tests
vi.doMock('@/lib/urls/utils', () => ({
getBaseUrl: vi.fn(() => 'http://localhost:3000'),
getBaseDomain: vi.fn(() => 'localhost:3000'),
getEmailDomain: vi.fn(() => 'localhost:3000'),
}))
mockSelect.mockReturnValue({ from: mockFrom })
mockFrom.mockReturnValue({ where: mockWhere })
mockWhere.mockReturnValue({ then: mockThen })

View File

@@ -12,6 +12,7 @@ import {
} from '@/lib/copilot/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { validateUUID } from '@/lib/security/input-validation'
import { getBaseUrl } from '@/lib/urls/utils'
const logger = createLogger('CheckpointRevertAPI')
@@ -93,7 +94,7 @@ export async function POST(request: NextRequest) {
}
const stateResponse = await fetch(
`${request.nextUrl.origin}/api/workflows/${checkpoint.workflowId}/state`,
`${getBaseUrl()}/api/workflows/${checkpoint.workflowId}/state`,
{
method: 'PUT',
headers: {

View File

@@ -29,6 +29,7 @@ export const SupabaseBlock: BlockConfig<SupabaseResponse> = {
{ label: 'Update a Row', id: 'update' },
{ label: 'Delete a Row', id: 'delete' },
{ label: 'Upsert a Row', id: 'upsert' },
{ label: 'Vector Search', id: 'vector_search' },
],
value: () => 'query',
},
@@ -381,6 +382,41 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
placeholder: '100',
condition: { field: 'operation', value: 'query' },
},
// Vector search operation fields
{
id: 'functionName',
title: 'Function Name',
type: 'short-input',
layout: 'full',
placeholder: 'match_documents',
condition: { field: 'operation', value: 'vector_search' },
required: true,
},
{
id: 'queryEmbedding',
title: 'Query Embedding',
type: 'code',
layout: 'full',
placeholder: '[0.1, 0.2, 0.3, ...]',
condition: { field: 'operation', value: 'vector_search' },
required: true,
},
{
id: 'matchThreshold',
title: 'Match Threshold (optional)',
type: 'short-input',
layout: 'full',
placeholder: '0.78',
condition: { field: 'operation', value: 'vector_search' },
},
{
id: 'matchCount',
title: 'Match Count (optional)',
type: 'short-input',
layout: 'full',
placeholder: '10',
condition: { field: 'operation', value: 'vector_search' },
},
],
tools: {
access: [
@@ -390,6 +426,7 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
'supabase_update',
'supabase_delete',
'supabase_upsert',
'supabase_vector_search',
],
config: {
tool: (params) => {
@@ -406,12 +443,14 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
return 'supabase_delete'
case 'upsert':
return 'supabase_upsert'
case 'vector_search':
return 'supabase_vector_search'
default:
throw new Error(`Invalid Supabase operation: ${params.operation}`)
}
},
params: (params) => {
const { operation, data, filter, ...rest } = params
const { operation, data, filter, queryEmbedding, ...rest } = params
// Parse JSON data if it's a string
let parsedData
@@ -435,6 +474,21 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
parsedFilter = filter.trim()
}
// Handle query embedding for vector search
let parsedQueryEmbedding
if (queryEmbedding && typeof queryEmbedding === 'string' && queryEmbedding.trim()) {
try {
parsedQueryEmbedding = JSON.parse(queryEmbedding)
} catch (parseError) {
const errorMsg = parseError instanceof Error ? parseError.message : 'Unknown JSON error'
throw new Error(
`Invalid query embedding format: ${errorMsg}. Please provide a valid array of numbers like [0.1, 0.2, 0.3].`
)
}
} else if (queryEmbedding && Array.isArray(queryEmbedding)) {
parsedQueryEmbedding = queryEmbedding
}
// Build params object, only including defined values
const result = { ...rest }
@@ -446,6 +500,10 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
result.filter = parsedFilter
}
if (parsedQueryEmbedding !== undefined) {
result.queryEmbedding = parsedQueryEmbedding
}
return result
},
},
@@ -462,6 +520,11 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
// Query operation inputs
orderBy: { type: 'string', description: 'Sort column' },
limit: { type: 'number', description: 'Result limit' },
// Vector search operation inputs
functionName: { type: 'string', description: 'PostgreSQL function name for vector search' },
queryEmbedding: { type: 'array', description: 'Query vector/embedding for similarity search' },
matchThreshold: { type: 'number', description: 'Minimum similarity threshold (0-1)' },
matchCount: { type: 'number', description: 'Maximum number of similar results to return' },
},
outputs: {
message: {

View File

@@ -2,7 +2,7 @@ import type { JSX, SVGProps } from 'react'
import type { ToolResponse } from '@/tools/types'
export type BlockIcon = (props: SVGProps<SVGSVGElement>) => JSX.Element
export type ParamType = 'string' | 'number' | 'boolean' | 'json'
export type ParamType = 'string' | 'number' | 'boolean' | 'json' | 'array'
export type PrimitiveValueType =
| 'string'
| 'number'

View File

@@ -407,16 +407,30 @@ async function parseWithMistralOCR(fileUrl: string, filename: string, mimeType:
try {
const response = await retryWithExponentialBackoff(
async () => {
const url =
let url =
typeof mistralParserTool.request!.url === 'function'
? mistralParserTool.request!.url(params)
: mistralParserTool.request!.url
const headers =
if (url.startsWith('/')) {
const { getBaseUrl } = await import('@/lib/urls/utils')
url = `${getBaseUrl()}${url}`
}
let headers =
typeof mistralParserTool.request!.headers === 'function'
? mistralParserTool.request!.headers(params)
: mistralParserTool.request!.headers
if (url.includes('/api/tools/mistral/parse')) {
const { generateInternalToken } = await import('@/lib/auth/internal')
const internalToken = await generateInternalToken()
headers = {
...headers,
Authorization: `Bearer ${internalToken}`,
}
}
const requestBody = mistralParserTool.request!.body!(params) as OCRRequestBody
return makeOCRRequest(url, headers as Record<string, string>, requestBody)
},

View File

@@ -227,7 +227,7 @@ export async function executeTool(
const isInternalRoute = endpointUrl.startsWith('/api/')
if (isInternalRoute || skipProxy) {
const result = await handleInternalRequest(toolId, tool, contextParams)
const result = await handleInternalRequest(toolId, tool, contextParams, executionContext)
// Apply post-processing if available and not skipped
let finalResult = result
@@ -414,7 +414,8 @@ function isErrorResponse(
async function handleInternalRequest(
toolId: string,
tool: ToolConfig,
params: Record<string, any>
params: Record<string, any>,
executionContext?: ExecutionContext
): Promise<ToolResponse> {
const requestId = generateRequestId()
@@ -427,7 +428,11 @@ async function handleInternalRequest(
const endpointUrl =
typeof tool.request.url === 'function' ? tool.request.url(params) : tool.request.url
const fullUrl = new URL(endpointUrl, baseUrl).toString()
const fullUrlObj = new URL(endpointUrl, baseUrl)
if (executionContext?.workflowId && typeof window === 'undefined') {
fullUrlObj.searchParams.set('workflowId', executionContext.workflowId)
}
const fullUrl = fullUrlObj.toString()
// For custom tools, validate parameters on the client side before sending
if (toolId.startsWith('custom_') && tool.request.body) {
@@ -445,10 +450,26 @@ async function handleInternalRequest(
}
}
const headers = new Headers(requestParams.headers)
const isInternalRoute = endpointUrl.startsWith('/api/')
if (typeof window === 'undefined') {
if (isInternalRoute) {
try {
const internalToken = await generateInternalToken()
headers.set('Authorization', `Bearer ${internalToken}`)
logger.info(`[${requestId}] Added internal auth token for ${toolId}`)
} catch (error) {
logger.error(`[${requestId}] Failed to generate internal token for ${toolId}:`, error)
}
} else {
logger.info(`[${requestId}] Skipping internal auth token for external URL: ${endpointUrl}`)
}
}
// Prepare request options
const requestOptions = {
method: requestParams.method,
headers: new Headers(requestParams.headers),
headers: headers,
body: requestParams.body,
}

View File

@@ -180,6 +180,7 @@ import {
supabaseQueryTool,
supabaseUpdateTool,
supabaseUpsertTool,
supabaseVectorSearchTool,
} from '@/tools/supabase'
import { tavilyExtractTool, tavilySearchTool } from '@/tools/tavily'
import {
@@ -271,6 +272,7 @@ export const tools: Record<string, ToolConfig> = {
supabase_update: supabaseUpdateTool,
supabase_delete: supabaseDeleteTool,
supabase_upsert: supabaseUpsertTool,
supabase_vector_search: supabaseVectorSearchTool,
typeform_responses: typeformResponsesTool,
typeform_files: typeformFilesTool,
typeform_insights: typeformInsightsTool,

View File

@@ -4,6 +4,7 @@ import { insertTool } from '@/tools/supabase/insert'
import { queryTool } from '@/tools/supabase/query'
import { updateTool } from '@/tools/supabase/update'
import { upsertTool } from '@/tools/supabase/upsert'
import { vectorSearchTool } from '@/tools/supabase/vector_search'
export const supabaseQueryTool = queryTool
export const supabaseInsertTool = insertTool
@@ -11,3 +12,4 @@ export const supabaseGetRowTool = getRowTool
export const supabaseUpdateTool = updateTool
export const supabaseDeleteTool = deleteTool
export const supabaseUpsertTool = upsertTool
export const supabaseVectorSearchTool = vectorSearchTool

View File

@@ -45,6 +45,15 @@ export interface SupabaseUpsertParams {
data: any
}
export interface SupabaseVectorSearchParams {
apiKey: string
projectId: string
functionName: string
queryEmbedding: number[]
matchThreshold?: number
matchCount?: number
}
export interface SupabaseBaseResponse extends ToolResponse {
output: {
message: string
@@ -65,4 +74,6 @@ export interface SupabaseDeleteResponse extends SupabaseBaseResponse {}
export interface SupabaseUpsertResponse extends SupabaseBaseResponse {}
export interface SupabaseVectorSearchResponse extends SupabaseBaseResponse {}
export interface SupabaseResponse extends SupabaseBaseResponse {}

View File

@@ -0,0 +1,125 @@
import type {
SupabaseVectorSearchParams,
SupabaseVectorSearchResponse,
} from '@/tools/supabase/types'
import type { ToolConfig } from '@/tools/types'
export const vectorSearchTool: ToolConfig<
SupabaseVectorSearchParams,
SupabaseVectorSearchResponse
> = {
id: 'supabase_vector_search',
name: 'Supabase Vector Search',
description: 'Perform similarity search using pgvector in a Supabase table',
version: '1.0',
params: {
projectId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Supabase project ID (e.g., jdrkgepadsdopsntdlom)',
},
functionName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'The name of the PostgreSQL function that performs vector search (e.g., match_documents)',
},
queryEmbedding: {
type: 'array',
required: true,
visibility: 'user-or-llm',
description: 'The query vector/embedding to search for similar items',
},
matchThreshold: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Minimum similarity threshold (0-1), typically 0.7-0.9',
},
matchCount: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results to return (default: 10)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Supabase service role secret key',
},
},
request: {
url: (params) => {
// Use RPC endpoint for calling PostgreSQL functions
return `https://${params.projectId}.supabase.co/rest/v1/rpc/${params.functionName}`
},
method: 'POST',
headers: (params) => ({
apikey: params.apiKey,
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params) => {
// Build the RPC call parameters
const rpcParams: Record<string, any> = {
query_embedding: params.queryEmbedding,
}
// Add optional parameters if provided
if (params.matchThreshold !== undefined) {
rpcParams.match_threshold = params.matchThreshold
}
if (params.matchCount !== undefined) {
rpcParams.match_count = params.matchCount
}
return rpcParams
},
},
transformResponse: async (response: Response) => {
let data
try {
data = await response.json()
} catch (parseError) {
throw new Error(`Failed to parse Supabase vector search response: ${parseError}`)
}
const resultCount = Array.isArray(data) ? data.length : 0
if (resultCount === 0) {
return {
success: true,
output: {
message: 'No similar vectors found matching the search criteria',
results: data,
},
error: undefined,
}
}
return {
success: true,
output: {
message: `Successfully found ${resultCount} similar vector${resultCount === 1 ? '' : 's'}`,
results: data,
},
error: undefined,
}
},
outputs: {
message: { type: 'string', description: 'Operation status message' },
results: {
type: 'array',
description:
'Array of records with similarity scores from the vector search. Each record includes a similarity field (0-1) indicating how similar it is to the query vector.',
},
},
}

View File

@@ -629,11 +629,20 @@ helm uninstall sim
For production deployments, make sure to:
1. **Change default secrets**: Update `BETTER_AUTH_SECRET` and `ENCRYPTION_KEY` with secure, randomly generated values
1. **Change default secrets**: Update `BETTER_AUTH_SECRET`, `ENCRYPTION_KEY`, and `INTERNAL_API_SECRET` with secure, randomly generated values using `openssl rand -hex 32`
2. **Use strong database passwords**: Set `postgresql.auth.password` to a strong password
3. **Enable TLS**: Configure `postgresql.tls.enabled=true` and provide proper certificates
4. **Configure ingress TLS**: Enable HTTPS with proper SSL certificates
**Required Secrets:**
- `BETTER_AUTH_SECRET`: Authentication JWT signing (minimum 32 characters)
- `ENCRYPTION_KEY`: Encrypts sensitive data like environment variables (minimum 32 characters)
- `INTERNAL_API_SECRET`: Internal service-to-service authentication (minimum 32 characters)
**Optional Security (Recommended for Production):**
- `CRON_SECRET`: Authenticates scheduled job requests to API endpoints (required only if `cronjobs.enabled=true`)
- `API_ENCRYPTION_KEY`: Encrypts API keys at rest in database (must be exactly 64 hex characters). If not set, API keys are stored in plain text. Generate using: `openssl rand -hex 32` (outputs 64 hex chars representing 32 bytes)
### Example secure values:
```yaml
@@ -641,6 +650,9 @@ app:
env:
BETTER_AUTH_SECRET: "your-secure-random-string-here"
ENCRYPTION_KEY: "your-secure-encryption-key-here"
INTERNAL_API_SECRET: "your-secure-internal-api-secret-here"
CRON_SECRET: "your-secure-cron-secret-here"
API_ENCRYPTION_KEY: "your-64-char-hex-string-for-api-key-encryption" # Optional but recommended
postgresql:
auth:

View File

@@ -32,8 +32,15 @@ app:
NEXT_PUBLIC_SOCKET_URL: "https://simstudio-ws.acme.com"
# Security settings (REQUIRED - replace with your own secure secrets)
# Generate using: openssl rand -hex 32
BETTER_AUTH_SECRET: "your-secure-production-auth-secret-here"
ENCRYPTION_KEY: "your-secure-production-encryption-key-here"
INTERNAL_API_SECRET: "your-secure-production-internal-api-secret-here"
CRON_SECRET: "your-secure-production-cron-secret-here"
# Optional: API Key Encryption (RECOMMENDED for production)
# Generate 64-character hex string using: openssl rand -hex 32
API_ENCRYPTION_KEY: "your-64-char-hex-api-encryption-key-here" # Optional but recommended
NODE_ENV: "production"
NEXT_TELEMETRY_DISABLED: "1"

View File

@@ -30,8 +30,15 @@ app:
NEXT_PUBLIC_SOCKET_URL: "https://simstudio-ws.acme.com"
# Security settings (REQUIRED - replace with your own secure secrets)
# Generate using: openssl rand -hex 32
BETTER_AUTH_SECRET: "your-secure-production-auth-secret-here"
ENCRYPTION_KEY: "your-secure-production-encryption-key-here"
INTERNAL_API_SECRET: "your-secure-production-internal-api-secret-here"
CRON_SECRET: "your-secure-production-cron-secret-here"
# Optional: API Key Encryption (RECOMMENDED for production)
# Generate 64-character hex string using: openssl rand -hex 32
API_ENCRYPTION_KEY: "your-64-char-hex-api-encryption-key-here" # Optional but recommended
NODE_ENV: "production"
NEXT_TELEMETRY_DISABLED: "1"

View File

@@ -26,8 +26,15 @@ app:
NEXT_PUBLIC_SOCKET_URL: "http://localhost:3002"
# Example secrets for development (replace with secure values for production)
# For production, generate using: openssl rand -hex 32
BETTER_AUTH_SECRET: "dev-32-char-auth-secret-not-secure-dev"
ENCRYPTION_KEY: "dev-32-char-encryption-key-not-secure"
INTERNAL_API_SECRET: "dev-32-char-internal-secret-not-secure"
CRON_SECRET: "dev-32-char-cron-secret-not-for-prod"
# Optional: API Key Encryption (leave empty for dev, encrypts API keys at rest)
# For production, generate 64-char hex using: openssl rand -hex 32
API_ENCRYPTION_KEY: "" # Optional - if not set, API keys stored in plain text
# Realtime service
realtime:

View File

@@ -26,8 +26,15 @@ app:
NEXT_PUBLIC_SOCKET_URL: "https://simstudio-ws.acme.com"
# Security settings (REQUIRED - replace with your own secure secrets)
# Generate using: openssl rand -hex 32
BETTER_AUTH_SECRET: "" # Set via --set flag or external secret manager
ENCRYPTION_KEY: "" # Set via --set flag or external secret manager
INTERNAL_API_SECRET: "" # Set via --set flag or external secret manager
CRON_SECRET: "" # Set via --set flag or external secret manager
# Optional: API Key Encryption (RECOMMENDED for production)
# Generate 64-character hex string using: openssl rand -hex 32
API_ENCRYPTION_KEY: "" # Optional but recommended - encrypts API keys at rest
NODE_ENV: "production"
NEXT_TELEMETRY_DISABLED: "1"
@@ -150,4 +157,7 @@ networkPolicy:
# --set externalDatabase.database="your-db-name" \
# --set app.env.BETTER_AUTH_SECRET="$(openssl rand -hex 32)" \
# --set app.env.ENCRYPTION_KEY="$(openssl rand -hex 32)" \
# --set app.env.INTERNAL_API_SECRET="$(openssl rand -hex 32)" \
# --set app.env.CRON_SECRET="$(openssl rand -hex 32)" \
# --set app.env.API_ENCRYPTION_KEY="$(openssl rand -hex 32)" \
# --set realtime.env.BETTER_AUTH_SECRET="$(openssl rand -hex 32)"

View File

@@ -32,8 +32,15 @@ app:
NEXT_PUBLIC_SOCKET_URL: "https://simstudio-ws.acme.com"
# Security settings (REQUIRED - replace with your own secure secrets)
# Generate using: openssl rand -hex 32
BETTER_AUTH_SECRET: "your-secure-production-auth-secret-here"
ENCRYPTION_KEY: "your-secure-production-encryption-key-here"
INTERNAL_API_SECRET: "your-secure-production-internal-api-secret-here"
CRON_SECRET: "your-secure-production-cron-secret-here"
# Optional: API Key Encryption (RECOMMENDED for production)
# Generate 64-character hex string using: openssl rand -hex 32
API_ENCRYPTION_KEY: "your-64-char-hex-api-encryption-key-here" # Optional but recommended
NODE_ENV: "production"
NEXT_TELEMETRY_DISABLED: "1"

View File

@@ -27,8 +27,15 @@ app:
NEXT_PUBLIC_SOCKET_URL: "https://sim-ws.acme.ai"
# Security settings (REQUIRED - replace with your own secure secrets)
# Generate using: openssl rand -hex 32
BETTER_AUTH_SECRET: "your-production-auth-secret-here"
ENCRYPTION_KEY: "your-production-encryption-key-here"
INTERNAL_API_SECRET: "your-production-internal-api-secret-here"
CRON_SECRET: "your-production-cron-secret-here"
# Optional: API Key Encryption (RECOMMENDED for production)
# Generate 64-character hex string using: openssl rand -hex 32
API_ENCRYPTION_KEY: "your-64-char-hex-api-encryption-key-here" # Optional but recommended
# Email verification (set to true if you want to require email verification)
EMAIL_VERIFICATION_ENABLED: "false"

View File

@@ -20,8 +20,15 @@ app:
NEXT_PUBLIC_SOCKET_URL: "https://sim-ws.acme.ai"
# Security settings (REQUIRED)
# Generate using: openssl rand -hex 32
BETTER_AUTH_SECRET: "your-production-auth-secret-here"
ENCRYPTION_KEY: "your-production-encryption-key-here"
INTERNAL_API_SECRET: "your-production-internal-api-secret-here"
CRON_SECRET: "your-production-cron-secret-here"
# Optional: API Key Encryption (RECOMMENDED for production)
# Generate 64-character hex string using: openssl rand -hex 32
API_ENCRYPTION_KEY: "your-64-char-hex-api-encryption-key-here" # Optional but recommended
# UI Branding & Whitelabeling Configuration
NEXT_PUBLIC_BRAND_NAME: "Acme AI Studio"

View File

@@ -63,6 +63,15 @@ app:
# Generate secure 32-character secrets using: openssl rand -hex 32
BETTER_AUTH_SECRET: "" # REQUIRED - set via --set flag or external secret manager
ENCRYPTION_KEY: "" # REQUIRED - set via --set flag or external secret manager
INTERNAL_API_SECRET: "" # REQUIRED - set via --set flag or external secret manager, used for internal service-to-service authentication
# Optional: Scheduled Jobs Authentication
# Generate using: openssl rand -hex 32
CRON_SECRET: "" # OPTIONAL - required only if cronjobs.enabled=true, authenticates scheduled job requests
# Optional: API Key Encryption (RECOMMENDED for production)
# Generate 64-character hex string using: openssl rand -hex 32 (outputs 64 hex chars = 32 bytes)
API_ENCRYPTION_KEY: "" # OPTIONAL - encrypts API keys at rest, must be exactly 64 hex characters, if not set keys stored in plain text
# Email & Communication
EMAIL_VERIFICATION_ENABLED: "false" # Enable email verification for user registration and login (defaults to false)

View File

@@ -838,7 +838,7 @@ async function generateBlockDoc(blockPath: string, icons: Record<string, string>
return
}
if (blockConfig.type.includes('_trigger')) {
if (blockConfig.type.includes('_trigger') || blockConfig.type.includes('_webhook')) {
console.log(`Skipping ${blockConfig.type} - contains '_trigger'`)
return
}
@@ -1111,7 +1111,7 @@ function updateMetaJson() {
]
const metaJson = {
items,
pages: items,
}
fs.writeFileSync(metaJsonPath, JSON.stringify(metaJson, null, 2))