* fix(helm): preserve STS serviceName + networkPolicy.egress back-compat
Greptile flagged two real upgrade-breaking changes vs the prior chart:
1. statefulset-postgresql spec.serviceName flipped from <name>-postgresql
to <name>-postgresql-headless. spec.serviceName is immutable, so any
existing install would hit 'Forbidden: updates to statefulset spec ...'
on helm upgrade. Revert to the original name (the headless Service in
services.yaml is added alongside, not as a swap).
2. networkPolicy.egress changed from a list to a map ({extraRules, exceptCidrs}),
silently dropping any custom egress list set by existing users. Restore
the original list semantics for networkPolicy.egress and move cloud-metadata
blocking to a sibling top-level field networkPolicy.egressExceptCidrs.
Adds NOTES.txt upgrade-notes entry covering both + the ESO v1→v1beta1 default
flip (functionally a no-op, but worth surfacing).
* docs(helm): update README egress reference to new key name
* fix(helm): revert copilot-postgresql STS serviceName too (same immutability issue)
Audit caught that the main fix in d5c2e8ef5 missed statefulset-copilot-postgres.yaml,
which had the identical immutable-field rename from -copilot-postgresql to
-copilot-postgresql-headless. Same upgrade-break vector for anyone running
copilot.enabled=true on a prior chart version. Mirrors the fix and comment
from the main postgresql STS.
* improvement(helm): postgres startupProbe + otel-collector NetworkPolicy
- add startupProbe defaults for both postgresql + copilot-postgresql STSs
to shield liveness from slow first-boot (pgvector init, WAL replay)
- render a dedicated NetworkPolicy for the otel-collector when
telemetry.enabled=true (OTLP ingress from app/realtime/copilot, DNS +
HTTPS egress for forwarding to external observability backends)
- document why copilot + copilot-postgresql intentionally do NOT ship
dedicated NetworkPolicies (Redis URL is unknowable at render time)
- regression test pins the otel-collector NP at documentIndex 3
* test(helm): assert custom egress applied to realtime NP too
The prior test claimed coverage of both app and realtime NPs but only
asserted documentIndex 0. Split into two tests so a regression that drops
custom egress from realtime would fail loudly.
* docs(helm-skill): trim narrative bloat in values-model
Cut the historical 'Layer 2 was added in chart 1.0.0' note and the
generic 'single source of truth' framing. Kept the two actionable
points: ESO requires mapping Layer 1 keys; app.env overrides
envDefaults.
* improvement(helm): production-ready chart with security, ESO, and docs overhaul
Comprehensive Helm chart improvements bringing the chart up to industry
standards for security, secret management, and documentation.
Security
- Pod Security Standards "restricted" defaults on every pod and container
(runAsNonRoot, allowPrivilegeEscalation=false, capabilities.drop=[ALL],
seccompProfile=RuntimeDefault)
- automountServiceAccountToken=false on ServiceAccount and every pod
- NetworkPolicy egress blocks cloud metadata endpoints by default
- Sensitive app/realtime env keys auto-partitioned into chart-managed Secret
via envFrom; no more plaintext secrets on container specs
Secret management
- Three modes: inline, existingSecret, ExternalSecrets Operator (ESO)
- ESO sync supports arbitrary sensitive keys
- Fail-fast template rendering when ESO enabled but sensitive key unmapped
- AWS/Azure/GCP example files document all three modes
Reliability
- Headless Services for both Postgres StatefulSets
- HPA-aware replicas (omits spec.replicas when autoscaling.enabled)
- PodDisruptionBudget auto-activates when replicaCount > 1
- Startup / liveness / readiness probes with distinct timings
- CronJob ttlSecondsAfterFinished for automatic cleanup
Chart hygiene
- Image tags default to Chart.AppVersion; pullPolicy IfNotPresent
- Optional image.digest pin for content-addressed deploys
- kubeVersion >=1.25.0-0 enforced
- Ollama pinned to 0.23.2; mount moved to /data
Documentation
- README rewritten in cert-manager / Bitnami style
- NOTES.txt with post-install guidance
- Example values files annotated with usage and secret-strategy guidance
* fix(helm): correct resource names in README (sim-sim-* → sim-*)
The sim.fullname helper collapses to the release name when the release
name contains the chart name. With the documented release name 'sim',
actual resources are 'sim-app', 'sim-postgresql', etc. — not the
'sim-sim-*' form previously documented. Fixes copy-paste commands in the
pre-1.0.0 upgrade walkthrough and several troubleshooting snippets.
Also expands the cronjobs component description to reflect the full set
of 13 scheduled jobs (was understated as just Gmail/Outlook polling).
* improvement(helm): split app/realtime env into Secret-bound + inline defaults
- Add app.envDefaults / realtime.envDefaults for chart-shipped operational
tunables (rate limits, timeouts, IVM, feature-flag defaults, localhost URL
fallbacks). Rendered inline on the container, not into the Secret
- Remove operational defaults from app.env / realtime.env so the chart-managed
Secret stays minimal and External Secrets Operator users only map keys they
actually set, not every chart default
- Skip an envDefaults key when the user explicitly sets it in env (K8s `env`
overrides `envFrom`, so an inline default would otherwise mask a Secret
value at runtime)
- Relax values.schema.json to allow empty strings on NEXT_PUBLIC_APP_URL,
BETTER_AUTH_URL, NEXT_PUBLIC_SUPPORT_EMAIL (defaults supplied via envDefaults)
* fix(helm): address PR review — cronjob validation, ESO apiVersion, secret merge order, image guard
- CronJobs reference CRON_SECRET via secretKeyRef; fail-fast at template
time when cronjobs.enabled=true and app.env.CRON_SECRET is empty so users
get a clear error instead of a CreateContainerConfigError loop
- Default externalSecrets.apiVersion to "v1beta1" (supported by every ESO
release since v0.7). The previous "v1" default targets only ESO v0.17+
- Swap merge order in secrets-app.yaml so app.env wins over realtime.env
for shared keys (BETTER_AUTH_SECRET, BETTER_AUTH_URL, …) — both pods
consume the same Secret via envFrom, so the app value must be canonical
- Add `required` guard on sim.image so an empty tag + empty digest +
empty Chart.AppVersion surfaces as a clear template-time error instead
of rendering an invalid `repo:` reference
* fix(helm): require critical secrets to be mapped when ESO is enabled
Previously, enabling externalSecrets without mapping BETTER_AUTH_SECRET /
ENCRYPTION_KEY / INTERNAL_API_SECRET (and CRON_SECRET when cronjobs are
on) rendered cleanly but produced CrashLoopBackOff at runtime with
cryptic missing-env errors. Fail at template time instead.
* fix(helm): auto-enable PDB when HPA minReplicas > 1
Previously the auto-enable predicate only checked the static
app.replicaCount, which defaults to 1 even when autoscaling is on
(HPA owns spec.replicas). PDB now also activates when
autoscaling.enabled=true and minReplicas > 1.
* fix(helm): prevent realtime envDefaults from masking app.env Secret values; add StatefulSet upgrade NOTES
- Realtime override-skip now considers keys set in either app.env or
realtime.env. The shared app Secret is mounted via envFrom on the
realtime pod, so a key set in app.env (e.g. NEXT_PUBLIC_APP_URL) would
previously be masked by the realtime envDefault (inline env overrides
envFrom in K8s).
- NOTES.txt now prints a StatefulSet orphan-delete reminder on upgrade,
surfacing the immutable serviceName issue documented in the README.
* feat(helm): add Claude Skill for chart deployment
Adds a skill at helm/sim/.claude/skills/sim-helm/ that teaches agents how
to deploy and troubleshoot the Sim Helm chart: install path selection
(inline / existingSecret / ESO), secret generation, the values.yaml
four-layer mental model, common-failure troubleshooting, and the
pre-1.0.0 StatefulSet orphan-delete upgrade procedure.
Skill is loadable by Claude Code, Codex, and OpenCode via the standard
skills convention (directory name matches frontmatter name).
* docs(helm): add CRON_SECRET to TL;DR, dry-run, and example install headers
The validateSecrets guard requires CRON_SECRET when cronjobs.enabled=true
(the default), but the quickstart and example file install commands
omitted it — users following the docs hit a hard template-render failure.
Adds CRON_SECRET to README TL;DR, validate-the-install dry-run snippet,
and the install command headers in all example values files.
* fix(helm): require INTERNAL_API_SECRET in inline secret mode
The ESO coverage validator already required INTERNAL_API_SECRET, but the
inline validateSecrets path only checked BETTER_AUTH_SECRET, ENCRYPTION_KEY,
and CRON_SECRET — letting inline installs render successfully and then
crash at runtime when the realtime↔app shared auth secret was missing.
Adds the same fail-fast check to the inline path.
* docs(helm): surface INTERNAL_API_SECRET upgrade requirement in NOTES.txt
The new validateSecrets check makes app.env.INTERNAL_API_SECRET mandatory
on upgrade. Existing installs that never set it would hit a template
render failure with no in-context guidance. Adds an upgrade-only note
with the generation snippet and storage guidance alongside the existing
StatefulSet orphan-delete instructions.
* fix(helm): NetworkPolicy egress to OTEL collector + external-db example format
- Add app/realtime NetworkPolicy egress rules for the OpenTelemetry
collector pod on ports 4317 (OTLP gRPC) and 4318 (OTLP HTTP) when
telemetry.enabled=true. Without these, traces and metrics were silently
dropped with connection-refused errors when both telemetry and
networkPolicy were enabled.
- Migrate values-external-db.yaml from the legacy list-shaped egress
format to the new {exceptCidrs, extraRules} object. The list form would
replace the default object on merge and crash template rendering when
the chart tried to access .exceptCidrs on a list.
* fix(helm): NOTES.txt no longer prints false secret warning for ESO users
The secrets-empty warning only checked app.secrets.existingSecret.enabled
before scanning app.env. ESO users intentionally leave app.env empty —
secrets come from the ESO-synced Secret — so every ESO install/upgrade
printed a misleading 'pods will fail to start' warning.
Reorders the branches so externalSecrets.enabled takes precedence: ESO
users now see a confirmation message with kubectl commands to verify the
ExternalSecret has synced. The empty-app.env warning only fires when
both ESO and existingSecret are disabled.
* fix(helm): existingSecret mode no longer drops app.env / realtime.env values
In existingSecret mode the chart-managed Secret is not rendered, so non-empty
values in app.env / realtime.env had nowhere to land — yet the envDefaults
skip logic still suppressed the matching defaults. Result: keys like
NEXT_PUBLIC_APP_URL, BETTER_AUTH_URL, and NODE_ENV silently went missing
on both pods (the example values-existing-secret.yaml hit this directly).
Both app and realtime deployments now inline non-empty values from app.env
(plus realtime.env on the realtime container) when existingSecret is enabled
and ESO is not. Inline / ESO modes are unchanged: inline still flows through
the chart-managed Secret, ESO still owns the synced Secret.
* fix(helm): correct realtime env overlay + filter chart-computed keys in existingSecret mode
Realtime: Sprig merge gives the first source precedence and treats "" as a
real value, so realtime.env empty defaults for shared keys shadowed
non-empty app.env values. Replace with deepCopy($appEnv) base + manual
non-empty overlay of $rtEnv.
Both deployments: exclude DATABASE_URL/SOCKET_SERVER_URL/OLLAMA_URL from
the existingSecret inline path so user-supplied values can't override
chart-computed ones via last-wins env semantics.
* fix(helm): skip envDefaults in existingSecret mode + document egress rename
In existingSecret mode the user's pre-existing Secret is the source of
truth (loaded via envFrom). Inlining localhost envDefaults for URL keys
(BETTER_AUTH_URL, NEXT_PUBLIC_APP_URL, ALLOWED_ORIGINS) silently shadowed
the Secret-bound values because K8s env always wins over envFrom. Skip
envDefaults entirely on both deployments when existingSecret is enabled.
Also call out the networkPolicy.egress shape change (list -> map with
exceptCidrs + extraRules) in the NOTES.txt upgrade block so operators
migrate their custom rules rather than silently losing them.
* fix(helm): copy-pasteable install commands in copilot + ESO examples
values-copilot.yaml: the install header was missing every required
copilot.server.env.* secret (AGENT_API_DB_ENCRYPTION_KEY, INTERNAL_API_SECRET,
LICENSE_KEY, SIM_BASE_URL, SIM_AGENT_API_KEY, REDIS_URL, one model key) plus
copilot.postgresql.auth.password. Pasting it as-is failed at template render.
values-external-secrets.yaml: NEXT_PUBLIC_APP_URL, BETTER_AUTH_URL, etc. were
declared under app.env / realtime.env. In ESO mode the chart-managed Secret
isn't rendered, so the validator (rightly) rejects keys in app.env that
aren't mapped under externalSecrets.remoteRefs. Moved non-secret URL/config
to envDefaults, which is inlined and not subject to the ESO mapping rule.
* polish(helm): configurable NetworkPolicy ingress peers + clearer API_ENCRYPTION_KEY comment
- networkPolicy.ingressFrom lets operators scope the ingress-controller
rule to a specific namespace/podSelector. Defaults to a single empty
peer (`- {}`), which is the explicit form of "any source" — same
effective behavior as the old `from: []` but unambiguous across CNIs.
To restrict, override with e.g.:
networkPolicy:
ingressFrom:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
- API_ENCRYPTION_KEY comment: drop the "must be exactly 64 hex
characters" phrasing that sat awkwardly next to `openssl rand -hex 32`.
The generation command already produces the required length.
* test(helm): add helm-unittest suites + CI workflow + ci values matrix
- 7 helm-unittest suites covering smoke, validators, secret modes,
envDefaults secret-mode-aware inlining (round-9 regression net),
chart-computed env keys (round-8 regression net), NetworkPolicy
shape, and PDB/HPA conditional rendering (38 tests, ~265ms).
- ci/*.yaml render fixtures for default, production, existingSecret,
ESO, and external-db install modes.
- GitHub Actions workflow runs helm lint --strict, helm unittest,
helm template across the ci matrix, and kubeconform validation
against Kubernetes 1.30 schemas.
- CONTRIBUTING.md documents how to run the same gates locally.
* test(helm): add helm test hook + kind apiserver dry-run in CI
- New templates/tests/test-connection.yaml renders a Pod with
helm.sh/hook=test that wgets the app Service (and realtime when
enabled). Lets users run `helm test <release>` after install for
a real in-cluster connectivity check. Restricted PSS context.
- tests.* values block (image, timeoutSeconds, resources) is the
knob to disable or tune the probe; documented in values.schema.json.
- 3 helm-unittest tests cover the hook annotations, PSS context,
and tests.enabled=false skip path (41 tests total).
- New CI job spins up a kind v1.30 cluster and runs
`kubectl apply --dry-run=server` against the rendered manifests
for the CRD-free ci fixtures (default / existing-secret /
external-db). Catches admission and validation issues the static
kubeconform schema check can't see.
* chore(helm): remove pre-1.0.0 upgrade fluff + tighten .helmignore
This is the 1.0.0 release of the chart — there is no pre-1.0.0
predecessor for users to upgrade from, so all of the dedicated upgrade
narration was hypothetical.
- Drop the 'Upgrading from a pre-1.0.0 build' README section and the
matching troubleshooting entry.
- Drop the .Release.IsUpgrade block from NOTES.txt: items 5 (StatefulSet
orphan-delete), 6 (INTERNAL_API_SECRET 'new in 1.0.0'), 7
(networkPolicy.egress shape change). Each described a migration off a
chart version that never shipped.
- Delete references/upgrade-pre-1.0.0.md and remove the corresponding
pointers from SKILL.md.
- Anchor .helmignore patterns to chart root so /tests/ (unit suites)
and /examples/ are dropped from the packaged tarball without also
dropping templates/tests/ (the helm test hook).
* chore(helm): drop CI workflow + ci/ fixtures + CONTRIBUTING.md
The helm-unittest suites in helm/sim/tests/ and the helm test hook
in helm/sim/templates/tests/ stay — those are chart-internal quality
scaffolding, not CI. Removed:
- .github/workflows/helm-chart.yml
- helm/sim/ci/*.yaml (5 render fixtures used only by the workflow)
- helm/sim/CONTRIBUTING.md (mostly documented those gates)
- dead /ci/ and /CONTRIBUTING.md entries in .helmignore
* feat(helm): pod rollout on Secret change + topologySpreadConstraints
- Add checksum/secret pod annotations on app, realtime, and copilot
Deployments (plus checksum/config on app when branding ConfigMap is
enabled). Closes the long-standing footgun where 'helm upgrade' with
a changed Secret would silently leave pods running the old values
until a manual rollout restart.
- New top-level topologySpreadConstraints value (and sim.topologySpreadConstraints
helper) applied to app and realtime Deployments. Mirrors how affinity
and tolerations are plumbed; users supply their own labelSelector
to mirror Bitnami convention.
- 5 helm-unittest cases cover the checksum annotations and topology
spread rendering (46 tests total).
* fix(helm): drop empty-string shadowing in app/realtime env merge
Sprig 'merge' treats "" as a real value, so a default-empty
app.env.BETTER_AUTH_URL would shadow a non-empty realtime.env override
and the URL would never reach the rendered Secret. Replace 'merge'
with an explicit two-pass overlay that filters empties before writing,
mirroring the same pattern already used in deployment-realtime.yaml's
existingSecret block.
Adds two regression tests: realtime.env-only value reaches the Secret
when app.env is empty, and app.env still wins on collision when both
are non-empty (48 tests total).
* fix(helm): make topologySpreadConstraints per-component to match docstring
Greptile flagged that sim.topologySpreadConstraints helper docstring promised
per-component config (.Values.app, .Values.realtime, ...) but call sites
passed .Values, so any app.topologySpreadConstraints / realtime.topologySpreadConstraints
set by the user was silently dropped. The single global key also prevented
distinct app-vs-realtime spread rules.
Pass .Values.app / .Values.realtime to the helper at each call site; move
the top-level topologySpreadConstraints key into both component sections in
values.yaml. Adds a regression test that app constraints don't leak onto
the realtime pod.
* fix(helm): allow cron pods through app NetworkPolicy
Cursor flagged that when networkPolicy.enabled=true and cronjobs.enabled=true
(the recommended production config), the app NetworkPolicy only allowed
ingress from realtime and the ingress controller — silently blocking every
cron pod's HTTP call to /api/schedules/execute, webhook polls, etc. All 13
default cronjobs would fail.
Tag cron pods with a stable simstudio.ai/component-group: cronjob label so
the app NetworkPolicy can allow them with a single rule (no per-job
enumeration). Rule is conditional on cronjobs.enabled. Adds positive and
negative regression tests.
* feat(enterprise): add data drains for continuous export to S3 / webhook
* chore(data-drains): regenerate migration on top of staging + bump route baseline
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* docs(data-drains): clarify retention pairing is user-coupled, not enforced
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(data-drains): preserve explicit forcePathStyle=false + reserve x-sim-signature
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* test(data-drains): drift guard ensures every webhook header is reserved
Asserts that any header buildHeaders writes is rejected when reused as a
custom signatureHeader. Adding a new metadata header without mirroring it
into RESERVED_SIGNATURE_HEADER_NAMES now fails CI.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* feat(knowledge): expose Cohere reranker controls on knowledge block
Add a self-hosted Cohere API key field (mirroring the agent block's hosted-key
pattern), a configurable reranker input pool size (1-100), and surface
meta.warnings from Cohere rerank responses via logger.warn. All new contract
fields are optional and nullable for full backwards compatibility.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(knowledge): address PR feedback on Cohere reranker controls
- Drop required:true on apiKey field — server has BYOK→env→rotation fallback
chain, so self-hosted users with COHERE_API_KEY env should not be blocked
- Drop .min(1) on rerankerApiKey contract field so empty strings coerce to
undefined via the transform (matches the existing query field pattern)
- Log a warning when rerankerInputCount is clamped up to topK so users notice
their setting was overridden
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(knowledge): mirror agent block API key visibility for Cohere reranker
Restore required:true on the Cohere API Key field and hide it server-side via
a new NEXT_PUBLIC_COHERE_CONFIGURED public env flag — same pattern the Agent
block uses for Azure (NEXT_PUBLIC_AZURE_CONFIGURED). Self-hosters who set
COHERE_API_KEY in their environment also set NEXT_PUBLIC_COHERE_CONFIGURED=true,
which removes the field from the UI; everyone else sees a required field.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(knowledge): treat empty rerankerInputCount as unset
An empty string from the Documents Sent to Reranker input passed the
undefined/null guard, so Number('') = 0 → clamped to 1, sending only 1
document to the reranker instead of falling back to the 4× topK auto
default. Add the empty-string check to the guard.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* feat(table): make plan table limits configurable via env vars
* fix(table): coerce env table limits to number for skipValidation env
* improvement(env): extract envNumber helper for numeric env coercion
* improvement(knowledge): use envNumber helper for KB_CONFIG_* env reads
* fix(testing): add envNumber to env mock factory
* fix(env): allow zero in envNumber for max-throughput configs
* fix(env): add min option to envNumber for strict-positive configs
* fix(auth): resolve CORS errors for self-hosted deployments behind reverse proxies
- auth client now uses browser origin first, falling back to NEXT_PUBLIC_APP_URL
- socket client falls back to page origin when served from non-localhost (assumes /socket.io is proxied)
- add TRUSTED_ORIGINS env var to extend Better Auth trustedOrigins (apex+www, alias hostnames)
- warn at startup when NEXT_PUBLIC_APP_URL is localhost in production
- preprocess empty NEXT_PUBLIC_SOCKET_URL so docker-compose ${VAR:-} works
- migrate remaining uuid/nanoid/randomUUID usages to @sim/utils generateId/generateShortId
- extend generateShortId with optional alphabet param (rejection sampling)
- document TRUSTED_ORIGINS in .env.example, docker-compose.prod.yml, and helm values.yaml
Fixessimstudioai/sim#1243
* fix(auth): address PR review comments
* chore(env): drop unnecessary NEXT_PUBLIC_SOCKET_URL preprocess (skipValidation is true)
* fix(docker): include @sim/utils in migrations image
Migration scripts now import generateId from @sim/utils/id; without copying packages/utils into the image, bun install fails to resolve the workspace dep at build time and the import fails at runtime.
* fix(helm): remove unused NEXT_PUBLIC_SOCKET_URL from realtime sections
The realtime service never reads NEXT_PUBLIC_SOCKET_URL — its env schema
only includes BETTER_AUTH_URL, NEXT_PUBLIC_APP_URL, ALLOWED_ORIGINS,
BETTER_AUTH_SECRET, INTERNAL_API_SECRET, DATABASE_URL, and REDIS_URL.
Remove the dead config from all helm values files and the values schema.
* fix(helm): allow empty NEXT_PUBLIC_SOCKET_URL in values schema
The default in values.yaml is now "" (empty string), which falls back to
the page origin at runtime. The schema previously required a valid URI,
which would reject the default. Mirror the INTERNAL_API_BASE_URL pattern
using anyOf with const "". Also add TRUSTED_ORIGINS to the schema.
* docs(self-hosting): mark NEXT_PUBLIC_SOCKET_URL as optional
The page-origin fallback in getSocketUrl() means self-hosters no longer
need to set NEXT_PUBLIC_SOCKET_URL when realtime is on the same origin
as the app. Update docs to reflect this:
- Remove NEXT_PUBLIC_SOCKET_URL from .env scaffolding examples in
docker.mdx, platforms.mdx, environment-variables.mdx
- Mark the variable as Optional in the env vars table with the new
default behavior described
- Update troubleshooting to point at reverse-proxy /socket.io routing
rather than the env var
- Flip dev docker-compose defaults (local, ollama, devcontainer) from
http://localhost:3002 to empty for consistency with prod.yml; the
in-code localhost fallback handles the dev case identically
Applied across all 6 documentation languages (en/fr/de/ja/es/zh).
* chore: untrack and ignore .claude/scheduled_tasks.lock
* feat(workspaces): add workspace logo upload
* feat(workspaces): add workspace logo upload
* fix(workspaces): validate logoUrl accepts only paths or HTTPS URLs
* fix(workspaces): add admin authorization, audit log, and posthog event for workspace logo uploads
* lint
* fix: add WebP support and use refs pattern in useProfilePictureUpload
- Add image/webp to ACCEPTED_IMAGE_TYPES in useProfilePictureUpload
- Add image/webp to file input accept attributes in whitelabeling settings
- Refactor useProfilePictureUpload to use refs for onUpload, onError, and
currentImage callbacks, matching the established codebase pattern
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: restore cloudwatch/cloudformation files from staging
These files were accidentally regressed during rebase conflict resolution,
reverting changes from #4027. Restoring to staging versions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: add workspace_logo_uploaded to PostHogEventMap
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: separate workspaceId ref sync to prevent overwrite on re-render
Split the ref sync useEffect so workspaceIdRef only updates when the
workspaceId prop changes, not when onUpload/onError callbacks get new
references. Prevents setTargetWorkspaceId from being overwritten by
a re-render before the file upload completes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use Pick type for workspace dropdown in knowledge header
The shared Workspace type requires ownerId and other fields that aren't
available from the workspaces API response mapping. Use a Pick type to
accurately represent the subset of fields actually constructed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: replace raw fetch with useWorkspacesQuery in knowledge header
Remove useState + useEffect + fetch anti-pattern for loading workspaces.
Use useWorkspacesQuery from React Query with inline filter for write/admin
permissions. Eliminates ~30 lines of manual state management, any casts,
and the Pick type workaround.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat(ee): enterprise feature flags, permission group platform controls, audit logs ui, delete account
* fix(settings): improve sidebar skeleton fidelity and fix credit purchase org cache invalidation
- Bump skeleton icon and text from 16/14px to 24px to better match real nav item visual weight
- Add orgId support to usePurchaseCredits so org billing/subscription caches are invalidated on credit purchase, matching the pattern used by useUpgradeSubscription
- Polish ColorInput in whitelabeling settings with auto-prefix and select-on-focus UX
* revert(settings): remove delete account feature
* fix(settings): address pr review — atomic autoAddNewMembers, extract query hook, fix types and signal forwarding
* chore(helm): add CREDENTIAL_SETS_ENABLED to values.yaml
* fix(access-control): dynamic platform category columns, atomic permission group delete
* fix(access-control): restore triggers section in blocks tab
* fix(access-control): merge triggers into tools section in blocks tab
* upgrade tubro
* fix(access-control): fix Select All state when config has stale blacklisted provider IDs
* fix(access-control): derive platform Select All from features list; revert turbo schema version
* fix(access-control): fix blocks Select All check, filter empty platform columns
* revert(settings): restore original skeleton icon and text sizes
* feat(trigger): add Google Sheets, Drive, and Calendar polling triggers
Add polling triggers for Google Sheets (new rows), Google Drive (file
changes via changes.list API), and Google Calendar (event updates via
updatedMin). Each includes OAuth credential support, configurable
filters (event type, MIME type, folder, search term, render options),
idempotency, and first-poll seeding. Wire triggers into block configs
and regenerate integrations.json. Update add-trigger skill with polling
instructions and versioned block wiring guidance.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(polling): address PR review feedback for Google polling triggers
- Fix Drive cursor stall: use nextPageToken as resume point when
breaking early from pagination instead of re-using the original token
- Eliminate redundant Drive API call in Sheets poller by returning
modifiedTime from the pre-check function
- Add 403/429 rate-limit handling to Sheets API calls matching the
Calendar handler pattern
- Remove unused changeType field from DriveChangeEntry interface
- Rename triggers/google_drive to triggers/google-drive for consistency
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(polling): fix Drive pre-check never activating in Sheets poller
isDriveFileUnchanged short-circuited when lastModifiedTime was
undefined, never calling the Drive API — so currentModifiedTime
was never populated, creating a permanent chicken-and-egg loop.
Now always calls the Drive API and returns the modifiedTime
regardless of whether there's a previous value to compare against.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore(lint): fix import ordering in triggers registry
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(polling): address PR review feedback for Google polling handlers
- Fix fetchHeaderRow to throw on 403/429 rate limits instead of silently
returning empty headers (prevents rows from being processed without
headers and lastKnownRowCount from advancing past them permanently)
- Fix Drive pagination to avoid advancing resume cursor past sliced
changes (prevents permanent change loss when allChanges > maxFiles)
- Remove unused logger import from Google Drive trigger config
* fix(polling): prevent data loss on partial row failures and harden idempotency key
- Sheets: only advance lastKnownRowCount by processedCount when there
are failures, so failed rows are retried on the next poll cycle
(idempotency deduplicates already-processed rows on re-fetch)
- Drive: add fallback for change.time in idempotency key to prevent
key collisions if the field is ever absent from the API response
* fix(polling): remove unused variable and preserve lastModifiedTime on Drive API failure
- Remove unused `now` variable from Google Drive polling handler
- Preserve stored lastModifiedTime when Drive API pre-check fails
(previously wrote undefined, disabling the optimization until the
next successful Drive API call)
* fix(polling): don't advance state when all events fail across sheets, calendar, drive handlers
* fix(polling): retry failed idempotency keys, fix drive cursor overshoot, fix calendar inclusive updatedMin
* fix(polling): revert calendar timestamp on any failure, not just all-fail
* fix(polling): revert drive cursor on any failure, not just all-fail
* feat(triggers): add canonical selector toggle to google polling triggers
- Add 'trigger-advanced' mode to SubBlockConfig so canonical pairs work in trigger mode
- Fix buildCanonicalIndex: trigger-mode subblocks don't overwrite non-trigger basicId, deduplicate advancedIds from block spreads
- Update editor, subblock layout, and trigger config aggregation to include trigger-advanced subblocks
- Replace dropdown+fetchOptions in Calendar/Sheets/Drive pollers with file-selector (basic) + short-input (advanced) canonical pairs
- Add canonicalParamId: 'oauthCredential' to triggerCredentials for selector context resolution
- Update polling handlers to read canonical fallbacks (calendarId||manualCalendarId, etc.)
* test(blocks): handle trigger-advanced mode in canonical validation tests
* fix(triggers): handle trigger-advanced mode in deploy, preview, params, and copilot
* fix(polling): use position-only idempotency key for sheets rows
* fix(polling): don't advance calendar timestamp to client clock on empty poll
* fix(polling): remove extraneous comment from calendar poller
* fix(polling): drive cursor stall on full page, calendar latestUpdated past filtered events
* fix(polling): advance calendar cursor past fully-filtered event batches
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat(enterprise): cloud whitelabeling for enterprise orgs
* fix(enterprise): scope enterprise plan check to target org in whitelabel PUT
* fix(enterprise): use isOrganizationOnEnterprisePlan for org-scoped enterprise check
* fix(enterprise): allow clearing whitelabel fields and guard against empty update result
* fix(enterprise): remove webp from logo accept attribute to match upload hook validation
* improvement(billing): use isBillingEnabled instead of isProd for plan gate bypasses
* fix(enterprise): show whitelabeling nav item when billing is enabled on non-hosted environments
* fix(enterprise): accept relative paths for logoUrl since upload API returns /api/files/serve/ paths
* fix(whitelabeling): prevent logo flash on refresh by hiding logo while branding loads
* fix(whitelabeling): wire hover color through CSS token on tertiary buttons
* fix(whitelabeling): show sim logo by default, only replace when org logo loads
* fix(whitelabeling): cache org logo url in localstorage to eliminate flash on repeat visits
* feat(whitelabeling): add wordmark support with drag/drop upload
* updated turbo
* fix(whitelabeling): defer localstorage read to effect to prevent hydration mismatch
* fix(whitelabeling): use layout effect for cache read to eliminate logo flash before paint
* fix(whitelabeling): cache theme css to eliminate color flash before org settings resolve
* fix(whitelabeling): deduplicate HEX_COLOR_REGEX into lib/branding and remove mutation from useCallback deps
* fix(whitelabeling): use cookie-based SSR cache to eliminate brand flash on all page loads
* fix(whitelabeling): use !orgSettings condition to fix SSR brand cache injection
React Query returns isLoading: false with data: undefined during SSR, so the
previous brandingLoading condition was always false on the server — initialCache
was never injected into brandConfig. Changing to !orgSettings correctly applies
the cookie cache both during SSR and while the client-side query loads, eliminating
the logo flash on hard refresh.
* improvement(kb): deferred content fetching and metadata-based hashes for connectors
* fix(kb): remove message count from outlook contentHash to prevent list/get divergence
* fix(kb): increase outlook getDocument message limit from 50 to 250
* fix(kb): skip outlook messages without conversationId to prevent broken stubs
* fix(kb): scope outlook getDocument to same folder as listDocuments to prevent hash divergence
* fix(kb): add missing connector sync cron job to Helm values
The connector sync endpoint existed but had no cron job configured to trigger it,
meaning scheduled syncs would never fire.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat(auth): add DISABLE_GOOGLE_AUTH and DISABLE_GITHUB_AUTH env vars
* fix(auth): also disable server-side OAuth provider registration when flags are set
* lint
* fix: allow Bedrock provider to use AWS SDK default credential chain
Remove hard requirement for explicit AWS credentials in Bedrock provider.
When access key and secret key are not provided, the AWS SDK automatically
falls back to its default credential chain (env vars, instance profile,
ECS task role, EKS IRSA, SSO).
Closes#3694
Signed-off-by: majiayu000 <1835304752@qq.com>
* fix: add partial credential guard for Bedrock provider
Reject configurations where only one of bedrockAccessKeyId or
bedrockSecretKey is provided, preventing silent fallback to the
default credential chain with a potentially different identity.
Add tests covering all credential configuration scenarios.
Signed-off-by: majiayu000 <1835304752@qq.com>
* fix: clean up bedrock test lint and dead code
Remove unused config parameter and dead _lastConfig assignment
from mock factory. Break long mockReturnValue chain to satisfy
biome line-length rule.
Signed-off-by: majiayu000 <1835304752@qq.com>
* fix: address greptile review feedback on PR #3708
Use BedrockRuntimeClientConfig from SDK instead of inline type.
Add default return value for prepareToolsWithUsageControl mock.
Signed-off-by: majiayu000 <1835304752@qq.com>
* feat(providers): server-side credential hiding for Azure and Bedrock
* fix(providers): revert Bedrock credential fields to required with original placeholders
* fix(blocks): add hideWhenEnvSet to getProviderCredentialSubBlocks for Azure and Bedrock
* fix(agent): use getProviderCredentialSubBlocks() instead of duplicating credential subblocks
* fix(blocks): consolidate Vertex credential into shared factory with basic/advanced mode
* fix(types): resolve pre-existing TypeScript errors across auth, secrets, and copilot
* lint
* improvement(blocks): make Vertex AI project ID a password field
* fix(blocks): preserve vertexCredential subblock ID for backwards compatibility
* fix(blocks): follow canonicalParamId pattern correctly for vertex credential subblocks
* fix(blocks): keep vertexCredential subblock ID stable to preserve saved workflow state
* fix(blocks): add canonicalParamId to vertexCredential basic subblock to complete the swap pair
* fix types
* more types
---------
Signed-off-by: majiayu000 <1835304752@qq.com>
Co-authored-by: majiayu000 <1835304752@qq.com>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
* feat(turnstile): conditionally added CF turnstile to signup
* feat(auth): add execute-on-submit Turnstile, conditional harmony, and feature flag
- Switch Turnstile to execution: 'execute' mode so challenge runs on
form submit (fresh token every time, no expiry issues)
- Make emailHarmony conditional via SIGNUP_EMAIL_VALIDATION_ENABLED
feature flag so self-hosted users can opt out
- Add isSignupEmailValidationEnabled to feature-flags.ts following
existing pattern
- Add better-auth-harmony to Next.js transpilePackages (required for
validator.js ESM compatibility)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(validation): remove dead validateEmail and checkMXRecord
Server-side disposable email blocking is now handled by
better-auth-harmony. The async validateEmail (with MX check) had no
remaining callers. Only quickValidateEmail remains for client-side
form feedback.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(auth): add 15s timeout to Turnstile captcha promise
Prevents form from hanging indefinitely if Turnstile never fires
onSuccess/onError (e.g. script fails to load, network drop).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(helm): add Turnstile and harmony env vars to values.yaml
Adds TURNSTILE_SECRET_KEY, NEXT_PUBLIC_TURNSTILE_SITE_KEY, and
SIGNUP_EMAIL_VALIDATION_ENABLED to the helm chart so self-hosted
deployments can configure captcha and disposable email blocking.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(auth): reject captcha promise on token expiry
onExpire now rejects the pending promise so the form doesn't hang
if the Turnstile token expires mid-challenge.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(login): replace useEffect keydown listener with form onSubmit
The forgot-password modal used a global window keydown listener in a
useEffect to handle Enter key — a "you might not need an effect"
anti-pattern with a stale closure risk. Replaced with a native
<form onSubmit> wrapper which handles Enter natively, eliminating
the useEffect, the global listener, and the stale closure.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(auth): clear dangling timeout after captcha promise settles
Use .finally(() => clearTimeout(timeoutId)) to clean up the 15s
timeout timer when the captcha resolves before the deadline.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(auth): use getResponsePromise() for Turnstile token retrieval
Replace the manual Promise + refs + timeout pattern with the
documented getResponsePromise(timeout) API from @marsidev/react-turnstile.
This eliminates captchaToken state, captchaResolveRef, captchaRejectRef,
and all callback wiring on the Turnstile component.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(auth): show captcha errors as form-level message, not password error
Captcha failures were misleadingly displayed under the password field.
Added a dedicated formError state that renders above the submit button,
making it clear the issue is with verification, not the password.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Add DISABLE_PUBLIC_API / NEXT_PUBLIC_DISABLE_PUBLIC_API environment variables
and disablePublicApi permission group config option to allow self-hosted
deployments and enterprise admins to globally disable the public API toggle.
When disabled: the Access toggle is hidden in the Edit API Info modal,
the execute route blocks unauthenticated public access (401), and the
public-api PATCH route rejects enabling public API (403).
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat(access-control): add ALLOWED_INTEGRATIONS env var for self-hosted block restrictions
* fix(tests): add getAllowedIntegrationsFromEnv mock to agent-handler tests
* fix(access-control): add auth to allowlist endpoint, fix loading state race, use accurate error message
* fix(access-control): remove auth from allowed-integrations endpoint to match models endpoint pattern
* fix(access-control): normalize blockType to lowercase before env allowlist check
* fix(access-control): expose merged allowedIntegrations on config to prevent bypass via direct access
* consolidate merging of allowed blocks so all callers have it by default
* normalize to lower case
* added tests
* added tests, normalize to lower case
* added safety incase userId is missing
* fix failing tests
* fix(docs): update requirements to be more accurate for deploying the app
* updated kb to support 1536 dimension vectors for models other than text embedding 3 small
* fix(storage): support Azure connection string for presigned URLs
* fix(kb): update test for embedding dimensions parameter
* fix(storage): align credential source ordering for consistency
* improvement(helm): add internal ingress support and same-host path consolidation
* improvement(helm): clean up ingress template comments
Simplify verbose inline Helm comments and section dividers to match the
minimal style used in services.yaml.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(helm): add missing copilot path consolidation for realtime host
When copilot.host equals realtime.host but differs from app.host,
copilot paths were not being routed. Added logic to consolidate
copilot paths into the realtime rule for this scenario.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* improvement(helm): follow ingress best practices
- Remove orphan comments that appeared when services were disabled
- Add documentation about path ordering requirements
- Paths rendered in order: realtime, copilot, app (specific before catch-all)
- Clean template output matching industry Helm chart standards
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat(og): add opengraph images for templates, blogs, and updated existing opengraph image for all other pages
* added to workspace templates page as well
* ack PR comments