+ <% if meta.present? %>
+ <%= meta %>
+ <% end %>
+ <%= status_pill %>
+
+
+
+ <%= sync_action if sync_action %>
+
diff --git a/app/views/transactions/_transfer_match.html.erb b/app/views/transactions/_transfer_match.html.erb
index f2d39531d..c7c8cf8de 100644
--- a/app/views/transactions/_transfer_match.html.erb
+++ b/app/views/transactions/_transfer_match.html.erb
@@ -6,11 +6,21 @@
<%= icon "link-2", size: "sm", class: "text-secondary" %>
<% elsif transaction.transfer.pending? %>
-
- <%= t("transactions.transfer_match.auto_matched") %>
+
+ <%= render DS::Pill.new(
+ label: t("transactions.transfer_match.auto_matched"),
+ tone: :neutral,
+ marker: false,
+ show_dot: false
+ ) %>
-
- <%= t("transactions.transfer_match.auto_matched_short") %>
+
+ <%= render DS::Pill.new(
+ label: t("transactions.transfer_match.auto_matched_short"),
+ tone: :neutral,
+ marker: false,
+ show_dot: false
+ ) %>
<%= button_to transfer_path(transaction.transfer, transfer: { status: "confirmed" }),
diff --git a/bin/docker-entrypoint b/bin/docker-entrypoint
index 67ef49314..672521a98 100755
--- a/bin/docker-entrypoint
+++ b/bin/docker-entrypoint
@@ -1,5 +1,15 @@
#!/bin/bash -e
+# Use jemalloc to reduce memory fragmentation if available
+JEMALLOC="/usr/lib/$(uname -m)-linux-gnu/libjemalloc.so.2"
+if [ -f "$JEMALLOC" ] && [ -z "${DISABLE_JEMALLOC}" ]; then
+ export LD_PRELOAD="${LD_PRELOAD:+$LD_PRELOAD:}$JEMALLOC"
+else
+ [ -n "${DISABLE_JEMALLOC}" ] \
+ && echo "WARNING: jemalloc disabled via DISABLE_JEMALLOC" \
+ || echo "WARNING: jemalloc not found at $JEMALLOC, skipping"
+fi
+
# If running the rails server then create or migrate existing database
if [ "${1}" == "./bin/rails" ] && [ "${2}" == "server" ]; then
./bin/rails db:prepare
diff --git a/charts/sure/CHANGELOG.md b/charts/sure/CHANGELOG.md
index 0abee238d..490cbe136 100644
--- a/charts/sure/CHANGELOG.md
+++ b/charts/sure/CHANGELOG.md
@@ -5,16 +5,22 @@ All notable changes to the Sure Helm chart will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-## [Unreleased]
+## [0.7.1] - 2026-05-31]
### Changed
-- Bumped `pipelock.image.tag` from `2.0.0` to `2.2.0` (three minor releases behind latest). Floating `@v2` CI action pin picks up patch/minor updates automatically.
-- Refreshed pipelock feature notes in the chart README, `docs/hosting/pipelock.md`, and `pipelock.example.yaml` to reference the upstream changelog instead of a single version.
+- Bumped `pipelock.image.tag` from `2.2.0` to `2.5.0`. Picks up three releases of scanner, federation, and audit work — see the [pipelock changelog](https://github.com/luckyPipewrench/pipelock/blob/main/CHANGELOG.md) for the full surface.
+- Refreshed pipelock feature notes in the chart README, `docs/hosting/pipelock.md`, and `pipelock.example.yaml` to call out 2.5 highlights (Audit Packet v0, request-body prompt-injection blocking, SPIFFE-strict inbound envelopes, scanner attribution on MCP block receipts).
+- Expanded the `pipelock.extraConfig` escape-hatch comment to reference the new sections available in 2.5 (browser_shield, mediation_envelope, redaction, learn, media_policy, a2a_scanning, emit).
+- Defaulted `pipelock.mcpToolPolicy.enabled` to `false`. Pipelock 2.x rejects an enabled `mcp_tool_policy` with no rules, so the prior chart default (`enabled: true`, empty rules) hard-failed config validation on startup. Operators who want tool policy active must now set `enabled: true` and provide at least one entry in `pipelock.mcpToolPolicy.rules`.
### Added
+- `pipelock.requestBodyScanning` (pipelock 2.5+): structured config for scanning outbound request bodies for prompt-injection and bodies/sensitive headers for DLP payloads. Disabled by default to preserve existing chart behavior; opt in with `enabled: true`.
+- `pipelock.healthWatchdog` (pipelock 2.4+): structured config for the wedge-detection watchdog. Operators can opt into per-subsystem detail in `/health` responses via `exposeSubsystems: true`.
+- `pipelock.mcpToolPolicy.rules`: structured Helm values for rendering `mcp_tool_policy.rules`, including redirect-profile references.
+- `asserts.tpl` guard that fails `helm template`/`helm install` when `pipelock.mcpToolPolicy.enabled=true` is paired with an empty `rules` list, surfacing the pipelock validation error at render time instead of container startup.
- README: CI scan status badge for the pipelock workflow.
-## [0.6.9-alpha] - 2026-03-24
+## [0.6.9] - 2026-03-31
### Changed
- Bumped `pipelock.image.tag` from `1.5.0` to `2.0.0`
@@ -58,7 +64,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- Renamed `_asserts.tpl` to `asserts.tpl` — Helm's `_` prefix convention prevented guards from executing
-## [0.6.7-alpha] - 2026-01-10
+## [0.6.7] - 2026-01-31
### Added
- **Redis Sentinel support for Sidekiq high availability**: Application now automatically detects and configures Sidekiq to use Redis Sentinel when `redisOperator.mode=sentinel` and `redisOperator.sentinel.enabled=true`
diff --git a/charts/sure/Chart.yaml b/charts/sure/Chart.yaml
index 412a3c29e..7101091a8 100644
--- a/charts/sure/Chart.yaml
+++ b/charts/sure/Chart.yaml
@@ -2,8 +2,8 @@ apiVersion: v2
name: sure
description: Official Helm chart for deploying the Sure Rails app (web + Sidekiq) on Kubernetes with optional HA PostgreSQL (CloudNativePG) and Redis.
type: application
-version: 0.7.1-alpha.10
-appVersion: "0.7.1-alpha.10"
+version: 0.7.1-alpha.11
+appVersion: "0.7.1-alpha.11"
kubeVersion: ">=1.25.0-0"
diff --git a/charts/sure/README.md b/charts/sure/README.md
index 73d5eb464..42a6688c8 100644
--- a/charts/sure/README.md
+++ b/charts/sure/README.md
@@ -645,7 +645,7 @@ hpa:
- **Forward proxy** (port 8888): Scans outbound HTTPS from Faraday-based AI clients. Auto-injected via `HTTPS_PROXY` env vars when enabled.
- **MCP reverse proxy** (port 8889): Scans inbound MCP traffic from external AI assistants.
-Recent pipelock releases add enhanced tool poisoning detection (full JSON schema scanning), per-read kill switch preemption, trusted domain allowlisting, MCP tool redirect profiles, signed action receipts, per-pattern DLP warn mode, and the `pipelock posture verify` / `pipelock session` CLIs. Process sandboxing and attack simulation are also available via `extraConfig` and CLI. See the [pipelock changelog](https://github.com/luckyPipewrench/pipelock/releases) for details.
+Recent pipelock releases add the Audit Packet v0 schema and language-portable verifiers (Go, TypeScript, Rust), request-body prompt-injection blocking, SPIFFE-strict inbound mediation envelopes, scanner attribution on MCP block receipts, enhanced tool poisoning detection, per-read kill switch preemption, trusted domain allowlisting, MCP tool redirect profiles, signed action receipts, per-pattern DLP warn mode, learn-and-lock behavioural contracts, the wedge-detection health watchdog, and the `pipelock posture verify` / `pipelock session` / `pipelock doctor` CLIs. Browser Shield, process sandboxing, and attack simulation are available via `extraConfig` and CLI. See the [pipelock changelog](https://github.com/luckyPipewrench/pipelock/releases) for details.
### Enabling Pipelock
@@ -653,7 +653,7 @@ Recent pipelock releases add enhanced tool poisoning detection (full JSON schema
pipelock:
enabled: true
image:
- tag: "2.2.0"
+ tag: "2.5.0"
mode: balanced # strict, balanced, or audit
```
@@ -677,12 +677,45 @@ pipelock:
mcpToolPolicy:
enabled: true
action: redirect # or use per-rule action overrides
+ rules:
+ - name: redirect-fetch
+ toolPattern: "^(fetch|web_fetch)$"
+ action: redirect
+ redirectProfile: safe-fetch
redirectProfiles:
safe-fetch:
exec: ["/pipelock", "internal-redirect", "fetch-proxy"]
reason: "Route fetch calls through audited proxy"
```
+### Request body scanning (pipelock 2.5+)
+
+Pipelock 2.5 added prompt-injection detection on outbound request bodies (JSON, form-encoded, raw text, WebSocket frames). When enabled, findings hard-block non-provider destinations even when `action: warn`; trusted provider hosts (OpenAI, Anthropic, etc.) remain exempt through the response-scanning exemption list.
+
+```yaml
+pipelock:
+ requestBodyScanning:
+ enabled: true
+ action: warn # warn or block
+ maxBodyBytes: 5242880 # 5 MB; fail-closed above this
+ scanHeaders: true
+ headerMode: sensitive # "sensitive" or "all"
+```
+
+Disabled by default. Roll out with `action: warn` first to observe findings in logs without blocking, then flip to `action: block` once the false-positive rate is acceptable.
+
+### Health watchdog
+
+The wedge-detection watchdog returns 503 on `/health` when a subsystem heartbeat (proxy hot path, MCP listener, rules-engine reload watcher) goes stale. Enabled by default in pipelock; the chart exposes the controls so operators can opt into per-subsystem detail in the health payload:
+
+```yaml
+pipelock:
+ healthWatchdog:
+ enabled: true
+ intervalSeconds: 2
+ exposeSubsystems: true # adds per-subsystem boolean map to /health
+```
+
### Validating your config
Pipelock includes CLI tools for config validation:
@@ -693,6 +726,9 @@ pipelock simulate --config pipelock.yaml
# Score your config's security posture (0-100)
pipelock audit score --config pipelock.yaml
+
+# Report whether configured protections are actually enforceable
+pipelock doctor
```
### Exposing MCP to external AI assistants
@@ -811,7 +847,7 @@ See `values.yaml` for the complete configuration surface, including:
- `migrations.*`: strategy job or initContainer
- `simplefin.encryption.*`: enable + backfill options
- `cronjobs.*`: custom CronJobs
-- `pipelock.*`: AI agent security proxy (forward proxy, MCP reverse proxy, DLP, injection scanning, trusted domains, tool redirect profiles, logging, serviceMonitor, ingress, PDB, extraConfig)
+- `pipelock.*`: AI agent security proxy (forward proxy, MCP reverse proxy, DLP, injection scanning, request-body scanning, health watchdog, trusted domains, tool redirect profiles, logging, serviceMonitor, ingress, PDB, extraConfig)
- `service.*`, `ingress.*`, `serviceMonitor.*`, `hpa.*`
## Helm tests
diff --git a/charts/sure/templates/asserts.tpl b/charts/sure/templates/asserts.tpl
index 1d481c0e9..52b3af9ac 100644
--- a/charts/sure/templates/asserts.tpl
+++ b/charts/sure/templates/asserts.tpl
@@ -21,3 +21,18 @@ Mutual exclusivity and configuration guards
{{- if and $extEnabled (not $plEnabled) $requirePL -}}
{{- fail "pipelock.requireForExternalAssistant is true but pipelock.enabled is false. Enable pipelock (pipelock.enabled=true) when using rails.externalAssistant, or set pipelock.requireForExternalAssistant=false." -}}
{{- end -}}
+
+{{/*
+Pipelock 2.x rejects an enabled mcp_tool_policy with no rules; surface this
+at helm template time instead of waiting for the container to crash-loop.
+*/}}
+{{- if $plEnabled -}}
+{{- $mtp := .Values.pipelock.mcpToolPolicy | default (dict) -}}
+{{- if hasKey $mtp "enabled" -}}
+{{- if $mtp.enabled -}}
+{{- if eq (len ($mtp.rules | default (list))) 0 -}}
+{{- fail "pipelock.mcpToolPolicy.enabled=true requires at least one entry in pipelock.mcpToolPolicy.rules. Pipelock rejects an enabled tool policy with no rules." -}}
+{{- end -}}
+{{- end -}}
+{{- end -}}
+{{- end -}}
diff --git a/charts/sure/templates/pipelock-configmap.yaml b/charts/sure/templates/pipelock-configmap.yaml
index b9c8fa5b7..c77ee68ba 100644
--- a/charts/sure/templates/pipelock-configmap.yaml
+++ b/charts/sure/templates/pipelock-configmap.yaml
@@ -65,6 +65,34 @@
{{- $chainWindow = int (.Values.pipelock.toolChainDetection.windowSize | default 20) -}}
{{- $chainGap = int (.Values.pipelock.toolChainDetection.maxGap | default 3) -}}
{{- end -}}
+{{- $rbsEnabled := false -}}
+{{- $rbsAction := "warn" -}}
+{{- $rbsMaxBytes := 5242880 -}}
+{{- $rbsScanHeaders := true -}}
+{{- $rbsHeaderMode := "sensitive" -}}
+{{- if .Values.pipelock.requestBodyScanning -}}
+{{- if hasKey .Values.pipelock.requestBodyScanning "enabled" -}}
+{{- $rbsEnabled = .Values.pipelock.requestBodyScanning.enabled -}}
+{{- end -}}
+{{- $rbsAction = .Values.pipelock.requestBodyScanning.action | default "warn" -}}
+{{- $rbsMaxBytes = int (.Values.pipelock.requestBodyScanning.maxBodyBytes | default 5242880) -}}
+{{- if hasKey .Values.pipelock.requestBodyScanning "scanHeaders" -}}
+{{- $rbsScanHeaders = .Values.pipelock.requestBodyScanning.scanHeaders -}}
+{{- end -}}
+{{- $rbsHeaderMode = .Values.pipelock.requestBodyScanning.headerMode | default "sensitive" -}}
+{{- end -}}
+{{- $hwEnabled := true -}}
+{{- $hwInterval := 2 -}}
+{{- $hwExpose := false -}}
+{{- if .Values.pipelock.healthWatchdog -}}
+{{- if hasKey .Values.pipelock.healthWatchdog "enabled" -}}
+{{- $hwEnabled = .Values.pipelock.healthWatchdog.enabled -}}
+{{- end -}}
+{{- $hwInterval = int (.Values.pipelock.healthWatchdog.intervalSeconds | default 2) -}}
+{{- if hasKey .Values.pipelock.healthWatchdog "exposeSubsystems" -}}
+{{- $hwExpose = .Values.pipelock.healthWatchdog.exposeSubsystems -}}
+{{- end -}}
+{{- end -}}
{{- $logFormat := "json" -}}
{{- $logOutput := "stdout" -}}
{{- $logIncludeAllowed := false -}}
@@ -128,6 +156,25 @@ data:
mcp_tool_policy:
enabled: {{ $mcpPolicyEnabled }}
action: {{ $mcpPolicyAction }}
+{{- if and .Values.pipelock.mcpToolPolicy .Values.pipelock.mcpToolPolicy.rules }}
+ rules:
+{{- range .Values.pipelock.mcpToolPolicy.rules }}
+ - name: {{ required "pipelock.mcpToolPolicy.rules[].name is required" .name | quote }}
+ tool_pattern: {{ required "pipelock.mcpToolPolicy.rules[].toolPattern is required" .toolPattern | quote }}
+{{- if .argPattern }}
+ arg_pattern: {{ .argPattern | quote }}
+{{- end }}
+{{- if .argKey }}
+ arg_key: {{ .argKey | quote }}
+{{- end }}
+{{- if .action }}
+ action: {{ .action | quote }}
+{{- end }}
+{{- if .redirectProfile }}
+ redirect_profile: {{ .redirectProfile | quote }}
+{{- end }}
+{{- end }}
+{{- end }}
{{- if and .Values.pipelock.mcpToolPolicy .Values.pipelock.mcpToolPolicy.redirectProfiles }}
redirect_profiles:
{{- toYaml .Values.pipelock.mcpToolPolicy.redirectProfiles | nindent 8 }}
@@ -140,6 +187,16 @@ data:
action: {{ $chainAction }}
window_size: {{ $chainWindow }}
max_gap: {{ $chainGap }}
+ request_body_scanning:
+ enabled: {{ $rbsEnabled }}
+ action: {{ $rbsAction }}
+ max_body_bytes: {{ $rbsMaxBytes }}
+ scan_headers: {{ $rbsScanHeaders }}
+ header_mode: {{ $rbsHeaderMode }}
+ health_watchdog:
+ enabled: {{ $hwEnabled }}
+ interval_seconds: {{ $hwInterval }}
+ expose_subsystems: {{ $hwExpose }}
logging:
format: {{ $logFormat }}
output: {{ $logOutput }}
diff --git a/charts/sure/values.yaml b/charts/sure/values.yaml
index f4b54f69e..d01d0dc5e 100644
--- a/charts/sure/values.yaml
+++ b/charts/sure/values.yaml
@@ -497,7 +497,7 @@ pipelock:
enabled: false
image:
repository: ghcr.io/luckypipewrench/pipelock
- tag: "2.2.0"
+ tag: "2.5.0"
pullPolicy: IfNotPresent
imagePullSecrets: []
replicas: 1
@@ -530,8 +530,17 @@ pipelock:
originPolicy: rewrite # rewrite, forward, or strip
# MCP tool policy: pre-execution rules for tool calls (shell obfuscation, etc.)
mcpToolPolicy:
- enabled: true
+ # Enable only when at least one rule is configured; pipelock rejects an
+ # enabled mcp_tool_policy with no rules.
+ enabled: false
action: warn
+ # Example:
+ # rules:
+ # - name: redirect-fetch
+ # toolPattern: "^(fetch|web_fetch)$"
+ # action: redirect
+ # redirectProfile: safe-fetch
+ rules: []
# Redirect profiles: route matched tool calls to audited handler programs instead
# of blocking. The handler returns a synthetic MCP response. Fail-closed on error.
# Example:
@@ -550,6 +559,24 @@ pipelock:
action: warn
windowSize: 20
maxGap: 3
+ # Request body scanning (pipelock 2.5+): detect prompt-injection payloads
+ # in outbound request bodies (JSON, form-encoded, raw text, WebSocket frames).
+ # In enforce mode, prompt-injection findings hard-block non-provider
+ # destinations even when action is "warn"; trusted provider hosts (OpenAI,
+ # Anthropic, etc.) remain exempt via the response_scanning exemption list.
+ requestBodyScanning:
+ enabled: false
+ action: warn # warn or block
+ maxBodyBytes: 5242880 # 5 MB; fail-closed above this
+ scanHeaders: true # scan request headers for DLP
+ headerMode: sensitive # "sensitive" or "all"
+ # Health watchdog (pipelock 2.4+): /health returns 503 when any subsystem
+ # heartbeat goes stale. Enabled by default in pipelock; exposed here so
+ # operators can opt into per-subsystem detail in the health payload.
+ healthWatchdog:
+ enabled: true
+ intervalSeconds: 2
+ exposeSubsystems: false # include per-subsystem map in /health response
service:
type: ClusterIP
resources:
@@ -601,8 +628,10 @@ pipelock:
includeBlocked: true
# Escape hatch: ADDITIONAL config sections appended to pipelock.yaml.
- # Use for sections not covered by structured values above (session_profiling,
- # data_budget, adaptive_enforcement, kill_switch, sandbox, reverse_proxy, etc.)
+ # Use for sections not covered by structured values above. Examples:
+ # session_profiling, data_budget, adaptive_enforcement, kill_switch, sandbox,
+ # reverse_proxy, redaction, browser_shield, mediation_envelope, learn,
+ # media_policy, a2a_scanning, emit (OTel agent.threat.detection attributes).
# Do NOT duplicate keys already rendered above - behavior is parser-dependent.
extraConfig: {}
diff --git a/compose.example.ai.yml b/compose.example.ai.yml
index 7c79178f7..48a696f38 100644
--- a/compose.example.ai.yml
+++ b/compose.example.ai.yml
@@ -112,7 +112,7 @@ x-rails-env: &rails_env
services:
pipelock:
- image: ghcr.io/luckypipewrench/pipelock:latest # pin to a specific version (e.g., :2.2.0) for production
+ image: ghcr.io/luckypipewrench/pipelock:latest # pin to a specific version (e.g., :2.5.0) for production
container_name: pipelock
hostname: pipelock
restart: unless-stopped
diff --git a/config/locales/views/imports/de.yml b/config/locales/views/imports/de.yml
index bd733bc77..faab67cc0 100644
--- a/config/locales/views/imports/de.yml
+++ b/config/locales/views/imports/de.yml
@@ -61,13 +61,13 @@ de:
rows_label: Zeilen
unassigned_account: Neues Konto für nicht zugewiesene Zeilen erstellen?
show:
- account_mapping_description: Weise alle Konten aus deiner importierten Datei den bestehenden Konten in %{product} zu. Du kannst auch neue Konten hinzufügen oder sie ohne Kategorie lassen.
+ account_mapping_description: Weise alle Konten aus deiner importierten Datei den bestehenden Konten in %{product_name} zu. Du kannst auch neue Konten hinzufügen oder sie ohne Kategorie lassen.
account_mapping_title: Konten zuweisen
- account_type_mapping_description: Weise alle Kontotypen aus deiner importierten Datei den Kontotypen in %{product} zu.
+ account_type_mapping_description: Weise alle Kontotypen aus deiner importierten Datei den Kontotypen in %{product_name} zu.
account_type_mapping_title: Kontotypen zuweisen
- category_mapping_description: Weise alle Kategorien aus deiner importierten Datei den bestehenden Kategorien in %{product} zu. Du kannst auch neue Kategorien hinzufügen oder sie ohne Kategorie lassen.
+ category_mapping_description: Weise alle Kategorien aus deiner importierten Datei den bestehenden Kategorien in %{product_name} zu. Du kannst auch neue Kategorien hinzufügen oder sie ohne Kategorie lassen.
category_mapping_title: Kategorien zuweisen
- tag_mapping_description: Weise alle Tags aus deiner importierten Datei den bestehenden Tags in %{product} zu. Du kannst auch neue Tags hinzufügen oder sie ohne Kategorie lassen.
+ tag_mapping_description: Weise alle Tags aus deiner importierten Datei den bestehenden Tags in %{product_name} zu. Du kannst auch neue Tags hinzufügen oder sie ohne Kategorie lassen.
tag_mapping_title: Tags zuweisen
uploads:
show:
diff --git a/config/locales/views/imports/en.yml b/config/locales/views/imports/en.yml
index 270b7d2f6..d97b7cf21 100644
--- a/config/locales/views/imports/en.yml
+++ b/config/locales/views/imports/en.yml
@@ -155,17 +155,17 @@ en:
show:
invalid_data: "You have invalid data, please edit until all errors are resolved"
account_mapping_description: Assign all of your imported file's accounts to
- Maybe's existing accounts. You can also add new accounts or leave them
+ %{product_name}'s existing accounts. You can also add new accounts or leave them
uncategorized.
account_mapping_title: Assign your accounts
account_type_mapping_description: Assign all of your imported file's account
- types to Maybe's
+ types to %{product_name}'s
account_type_mapping_title: Assign your account types
category_mapping_description: Assign all of your imported file's categories
- to Maybe's existing categories. You can also add new categories or leave
+ to %{product_name}'s existing categories. You can also add new categories or leave
them uncategorized.
category_mapping_title: Assign your categories
- tag_mapping_description: Assign all of your imported file's tags to Maybe's
+ tag_mapping_description: Assign all of your imported file's tags to %{product_name}'s
existing tags. You can also add new tags or leave them uncategorized.
tag_mapping_title: Assign your tags
uploads:
diff --git a/config/locales/views/imports/es.yml b/config/locales/views/imports/es.yml
index b8f0c9ca5..d42aa010d 100644
--- a/config/locales/views/imports/es.yml
+++ b/config/locales/views/imports/es.yml
@@ -61,13 +61,13 @@ es:
rows_label: Filas
unassigned_account: ¿Necesitas crear una nueva cuenta para las filas sin asignar?
show:
- account_mapping_description: Asigna todas las cuentas de tu archivo importado a las cuentas existentes de Sure. También puedes añadir nuevas cuentas o dejarlas sin categorizar.
+ account_mapping_description: Asigna todas las cuentas de tu archivo importado a las cuentas existentes de %{product_name}. También puedes añadir nuevas cuentas o dejarlas sin categorizar.
account_mapping_title: Asigna tus cuentas
- account_type_mapping_description: Asigna todos los tipos de cuenta de tu archivo importado a los de Sure.
+ account_type_mapping_description: Asigna todos los tipos de cuenta de tu archivo importado a los de %{product_name}.
account_type_mapping_title: Asigna tus tipos de cuenta
- category_mapping_description: Asigna todas las categorías de tu archivo importado a las categorías existentes de Sure. También puedes añadir nuevas categorías o dejarlas sin categorizar.
+ category_mapping_description: Asigna todas las categorías de tu archivo importado a las categorías existentes de %{product_name}. También puedes añadir nuevas categorías o dejarlas sin categorizar.
category_mapping_title: Asigna tus categorías
- tag_mapping_description: Asigna todas las etiquetas de tu archivo importado a las etiquetas existentes de Sure. También puedes añadir nuevas etiquetas o dejarlas sin categorizar.
+ tag_mapping_description: Asigna todas las etiquetas de tu archivo importado a las etiquetas existentes de %{product_name}. También puedes añadir nuevas etiquetas o dejarlas sin categorizar.
tag_mapping_title: Asigna tus etiquetas
uploads:
show:
diff --git a/config/locales/views/imports/hu.yml b/config/locales/views/imports/hu.yml
index 8221e18fe..543194625 100644
--- a/config/locales/views/imports/hu.yml
+++ b/config/locales/views/imports/hu.yml
@@ -134,13 +134,13 @@ hu:
next: Tovább
show:
invalid_data: "Érvénytelen adatok találhatók, kérjük szerkeszd, amíg minden hibát ki nem javítasz"
- account_mapping_description: Rendeld hozzá az importált fájl számláit a Maybe meglévő számláihoz. Új számlákat is hozzáadhatsz, vagy hagyhatod őket kategorizálatlanul.
+ account_mapping_description: Rendeld hozzá az importált fájl számláit a %{product_name} meglévő számláihoz. Új számlákat is hozzáadhatsz, vagy hagyhatod őket kategorizálatlanul.
account_mapping_title: Számlák hozzárendelése
- account_type_mapping_description: Rendeld hozzá az importált fájl számlatípusait a Maybe típusaihoz.
+ account_type_mapping_description: Rendeld hozzá az importált fájl számlatípusait a %{product_name} típusaihoz.
account_type_mapping_title: Számlatípusok hozzárendelése
- category_mapping_description: Rendeld hozzá az importált fájl kategóriáit a Maybe meglévő kategóriáihoz. Új kategóriákat is hozzáadhatsz, vagy hagyhatod őket kategorizálatlanul.
+ category_mapping_description: Rendeld hozzá az importált fájl kategóriáit a %{product_name} meglévő kategóriáihoz. Új kategóriákat is hozzáadhatsz, vagy hagyhatod őket kategorizálatlanul.
category_mapping_title: Kategóriák hozzárendelése
- tag_mapping_description: Rendeld hozzá az importált fájl címkéit a Maybe meglévő címkéihez. Új címkéket is hozzáadhatsz, vagy hagyhatod őket kategorizálatlanul.
+ tag_mapping_description: Rendeld hozzá az importált fájl címkéit a %{product_name} meglévő címkéihez. Új címkéket is hozzáadhatsz, vagy hagyhatod őket kategorizálatlanul.
tag_mapping_title: Címkék hozzárendelése
uploads:
update:
diff --git a/config/locales/views/imports/nb.yml b/config/locales/views/imports/nb.yml
index cebd1ddb0..3e4f60610 100644
--- a/config/locales/views/imports/nb.yml
+++ b/config/locales/views/imports/nb.yml
@@ -31,18 +31,18 @@ nb:
unassigned_account: Trenger å opprette en ny konto for utilordnede rader?
show:
account_mapping_description: Tilordne alle kontoene i den importerte filen din til
- Maybes eksisterende kontoer. Du kan også legge til nye kontoer eller la dem
+ %{product_name}s eksisterende kontoer. Du kan også legge til nye kontoer eller la dem
være ukategorisert.
account_mapping_title: Tilordne kontoene dine
account_type_mapping_description: Tilordne alle kontotypene i den importerte filen din til
- Maybes
+ %{product_name}s
account_type_mapping_title: Tilordne kontotypene dine
category_mapping_description: Tilordne alle kategoriene i den importerte filen din til
- Maybes eksisterende kategorier. Du kan også legge til nye kategorier eller la dem
+ %{product_name}s eksisterende kategorier. Du kan også legge til nye kategorier eller la dem
være ukategorisert.
category_mapping_title: Tilordne kategoriene dine
tag_mapping_description: Tilordne alle tagene i den importerte filen din til
- Maybes eksisterende tagger. Du kan også legge til nye tagger eller la dem
+ %{product_name}s eksisterende tagger. Du kan også legge til nye tagger eller la dem
være ukategorisert.
tag_mapping_title: Tilordne tagene dine
uploads:
diff --git a/config/locales/views/imports/nl.yml b/config/locales/views/imports/nl.yml
index 3d40a4e90..31a2d3678 100644
--- a/config/locales/views/imports/nl.yml
+++ b/config/locales/views/imports/nl.yml
@@ -37,13 +37,13 @@ nl:
rows_label: Rijen
unassigned_account: Moet u een nieuw account aanmaken voor niet-toegewezen rijen?
show:
- account_mapping_description: Wijs alle accounts van uw geïmporteerde bestand toe aan bestaande %{product} accounts. U kunt ook nieuwe accounts toevoegen of ze ongecategoriseerd laten.
+ account_mapping_description: Wijs alle accounts van uw geïmporteerde bestand toe aan bestaande %{product_name} accounts. U kunt ook nieuwe accounts toevoegen of ze ongecategoriseerd laten.
account_mapping_title: Uw accounts toewijzen
- account_type_mapping_description: Wijs alle accounttypen van uw geïmporteerde bestand toe aan %{product} accounttypen
+ account_type_mapping_description: Wijs alle accounttypen van uw geïmporteerde bestand toe aan %{product_name} accounttypen
account_type_mapping_title: Uw accounttypen toewijzen
- category_mapping_description: Wijs alle categorieën van uw geïmporteerde bestand toe aan bestaande %{product} categorieën. U kunt ook nieuwe categorieën toevoegen of ze ongecategoriseerd laten.
+ category_mapping_description: Wijs alle categorieën van uw geïmporteerde bestand toe aan bestaande %{product_name} categorieën. U kunt ook nieuwe categorieën toevoegen of ze ongecategoriseerd laten.
category_mapping_title: Uw categorieën toewijzen
- tag_mapping_description: Wijs alle tags van uw geïmporteerde bestand toe aan bestaande %{product} tags. U kunt ook nieuwe tags toevoegen of ze ongecategoriseerd laten.
+ tag_mapping_description: Wijs alle tags van uw geïmporteerde bestand toe aan bestaande %{product_name} tags. U kunt ook nieuwe tags toevoegen of ze ongecategoriseerd laten.
tag_mapping_title: Uw tags toewijzen
uploads:
show:
diff --git a/config/locales/views/imports/pl.yml b/config/locales/views/imports/pl.yml
index af8db190c..d5d1cd967 100644
--- a/config/locales/views/imports/pl.yml
+++ b/config/locales/views/imports/pl.yml
@@ -74,13 +74,13 @@ pl:
rows_label: Wiersze
unassigned_account: Chcesz utworzyć nowe konto dla nieprzypisanych wierszy?
show:
- account_mapping_description: Przypisz wszystkie konta z importowanego pliku do istniejących kont %{product}. Możesz też dodać nowe konta lub zostawić je bez przypisania.
+ account_mapping_description: Przypisz wszystkie konta z importowanego pliku do istniejących kont %{product_name}. Możesz też dodać nowe konta lub zostawić je bez przypisania.
account_mapping_title: Przypisz konta
- account_type_mapping_description: Przypisz wszystkie typy kont z importowanego pliku do typów używanych w %{product}.
+ account_type_mapping_description: Przypisz wszystkie typy kont z importowanego pliku do typów używanych w %{product_name}.
account_type_mapping_title: Przypisz typy kont
- category_mapping_description: Przypisz wszystkie kategorie z importowanego pliku do istniejących kategorii %{product}. Możesz też dodać nowe kategorie lub zostawić je bez przypisania.
+ category_mapping_description: Przypisz wszystkie kategorie z importowanego pliku do istniejących kategorii %{product_name}. Możesz też dodać nowe kategorie lub zostawić je bez przypisania.
category_mapping_title: Przypisz kategorie
- tag_mapping_description: Przypisz wszystkie tagi z importowanego pliku do istniejących tagów %{product}. Możesz też dodać nowe tagi lub zostawić je bez przypisania.
+ tag_mapping_description: Przypisz wszystkie tagi z importowanego pliku do istniejących tagów %{product_name}. Możesz też dodać nowe tagi lub zostawić je bez przypisania.
tag_mapping_title: Przypisz tagi
uploads:
show:
diff --git a/config/locales/views/imports/pt-BR.yml b/config/locales/views/imports/pt-BR.yml
index 15b71dc0e..84edb7b83 100644
--- a/config/locales/views/imports/pt-BR.yml
+++ b/config/locales/views/imports/pt-BR.yml
@@ -36,18 +36,18 @@ pt-BR:
unassigned_account: Precisa criar uma nova conta para linhas não atribuídas?
show:
account_mapping_description: Atribua todas as contas do seu arquivo importado às
- contas existentes no %{product}. Você também pode adicionar novas contas ou deixá-las
+ contas existentes no %{product_name}. Você também pode adicionar novas contas ou deixá-las
sem categoria.
account_mapping_title: Atribuir suas contas
account_type_mapping_description: Atribuir todos os tipos de conta do seu arquivo importado aos
- do %{product}
+ do %{product_name}
account_type_mapping_title: Atribuir seus tipos de conta
category_mapping_description: Atribua todas as categorias do seu arquivo importado às
- categorias existentes no %{product}. Você também pode adicionar novas categorias ou deixá-las
+ categorias existentes no %{product_name}. Você também pode adicionar novas categorias ou deixá-las
sem categoria.
category_mapping_title: Atribuir suas categorias
tag_mapping_description: Atribua todas as tags do seu arquivo importado às
- tags existentes no %{product}. Você também pode adicionar novas tags ou deixá-las
+ tags existentes no %{product_name}. Você também pode adicionar novas tags ou deixá-las
sem categoria.
tag_mapping_title: Atribuir suas tags
uploads:
diff --git a/config/locales/views/imports/ro.yml b/config/locales/views/imports/ro.yml
index bec1cf695..b9783f815 100644
--- a/config/locales/views/imports/ro.yml
+++ b/config/locales/views/imports/ro.yml
@@ -26,13 +26,13 @@ ro:
rows_label: Rânduri
unassigned_account: Trebuie să creezi un cont nou pentru rândurile neasignate?
show:
- account_mapping_description: Asignează toate conturile din fișierul tău importat conturilor existente din Maybe. Poți adăuga și conturi noi sau le poți lăsa necategorizate.
+ account_mapping_description: Asignează toate conturile din fișierul tău importat conturilor existente din %{product_name}. Poți adăuga și conturi noi sau le poți lăsa necategorizate.
account_mapping_title: Asignează-ți conturile
- account_type_mapping_description: Asignează toate tipurile de cont din fișierul tău importat celor din Maybe.
+ account_type_mapping_description: Asignează toate tipurile de cont din fișierul tău importat celor din %{product_name}.
account_type_mapping_title: Asignează-ți tipurile de cont
- category_mapping_description: Asignează toate categoriile din fișierul tău importat categoriilor existente din Maybe. Poți adăuga și categorii noi sau le poți lăsa necategorizate.
+ category_mapping_description: Asignează toate categoriile din fișierul tău importat categoriilor existente din %{product_name}. Poți adăuga și categorii noi sau le poți lăsa necategorizate.
category_mapping_title: Asignează-ți categoriile
- tag_mapping_description: Asignează toate etichetele din fișierul tău importat etichetelor existente din Maybe. Poți adăuga și etichete noi sau le poți lăsa necategorizate.
+ tag_mapping_description: Asignează toate etichetele din fișierul tău importat etichetelor existente din %{product_name}. Poți adăuga și etichete noi sau le poți lăsa necategorizate.
tag_mapping_title: Asignează-ți etichetele
uploads:
show:
diff --git a/config/locales/views/imports/tr.yml b/config/locales/views/imports/tr.yml
index 7aaaaf079..8296d655c 100644
--- a/config/locales/views/imports/tr.yml
+++ b/config/locales/views/imports/tr.yml
@@ -26,13 +26,13 @@ tr:
rows_label: Satırlar
unassigned_account: Atanmamış satırlar için yeni bir hesap oluşturmak ister misiniz?
show:
- account_mapping_description: "İçe aktardığınız dosyadaki tüm hesapları %{product}'deki mevcut hesaplara eşleyin. Ayrıca yeni hesaplar ekleyebilir veya kategorize etmeden bırakabilirsiniz."
+ account_mapping_description: "İçe aktardığınız dosyadaki tüm hesapları %{product_name}'deki mevcut hesaplara eşleyin. Ayrıca yeni hesaplar ekleyebilir veya kategorize etmeden bırakabilirsiniz."
account_mapping_title: Hesaplarınızı eşleyin
- account_type_mapping_description: "İçe aktardığınız dosyadaki tüm hesap türlerini %{product}'deki hesap türlerine eşleyin."
+ account_type_mapping_description: "İçe aktardığınız dosyadaki tüm hesap türlerini %{product_name}'deki hesap türlerine eşleyin."
account_type_mapping_title: Hesap türlerinizi eşleyin
- category_mapping_description: "İçe aktardığınız dosyadaki tüm kategorileri %{product}'deki mevcut kategorilere eşleyin. Ayrıca yeni kategoriler ekleyebilir veya kategorize etmeden bırakabilirsiniz."
+ category_mapping_description: "İçe aktardığınız dosyadaki tüm kategorileri %{product_name}'deki mevcut kategorilere eşleyin. Ayrıca yeni kategoriler ekleyebilir veya kategorize etmeden bırakabilirsiniz."
category_mapping_title: Kategorilerinizi eşleyin
- tag_mapping_description: "İçe aktardığınız dosyadaki tüm etiketleri %{product}'deki mevcut etiketlere eşleyin. Ayrıca yeni etiketler ekleyebilir veya kategorize etmeden bırakabilirsiniz."
+ tag_mapping_description: "İçe aktardığınız dosyadaki tüm etiketleri %{product_name}'deki mevcut etiketlere eşleyin. Ayrıca yeni etiketler ekleyebilir veya kategorize etmeden bırakabilirsiniz."
tag_mapping_title: Etiketlerinizi eşleyin
uploads:
show:
diff --git a/config/locales/views/imports/zh-CN.yml b/config/locales/views/imports/zh-CN.yml
index 3c5493f86..8ea66765b 100644
--- a/config/locales/views/imports/zh-CN.yml
+++ b/config/locales/views/imports/zh-CN.yml
@@ -34,13 +34,13 @@ zh-CN:
sure_mapping_label: "%{product_name} 中的 %{mapping}"
unassigned_account: 需要为未分配行创建新账户?
show:
- account_mapping_description: 将导入文件中的所有账户分配到 %{product} 的现有账户。您也可以新建账户或保留为未分类。
+ account_mapping_description: 将导入文件中的所有账户分配到 %{product_name} 的现有账户。您也可以新建账户或保留为未分类。
account_mapping_title: 分配账户
- account_type_mapping_description: 将导入文件中的所有账户类型分配到 %{product} 的账户类型
+ account_type_mapping_description: 将导入文件中的所有账户类型分配到 %{product_name} 的账户类型
account_type_mapping_title: 分配账户类型
- category_mapping_description: 将导入文件中的所有分类分配到 %{product} 的现有分类。您也可以新建分类或保留为未分类。
+ category_mapping_description: 将导入文件中的所有分类分配到 %{product_name} 的现有分类。您也可以新建分类或保留为未分类。
category_mapping_title: 分配分类
- tag_mapping_description: 将导入文件中的所有标签分配到 %{product} 的现有标签。您也可以新建标签或保留为未分类。
+ tag_mapping_description: 将导入文件中的所有标签分配到 %{product_name} 的现有标签。您也可以新建标签或保留为未分类。
tag_mapping_title: 分配标签
uploads:
show:
diff --git a/config/locales/views/imports/zh-TW.yml b/config/locales/views/imports/zh-TW.yml
index a7a47849b..6dbbba204 100644
--- a/config/locales/views/imports/zh-TW.yml
+++ b/config/locales/views/imports/zh-TW.yml
@@ -34,13 +34,13 @@ zh-TW:
rows_label: 資料列
unassigned_account: 需要為未分配的資料列建立新帳戶嗎?
show:
- account_mapping_description: 將匯入檔案中的帳戶對應到 Maybe 現有的帳戶。您也可以新增帳戶,或暫時不進行分類。
+ account_mapping_description: 將匯入檔案中的帳戶對應到 %{product_name} 現有的帳戶。您也可以新增帳戶,或暫時不進行分類。
account_mapping_title: 分配您的帳戶
- account_type_mapping_description: 將匯入檔案中的帳戶類型對應到 Maybe 的帳戶類型
+ account_type_mapping_description: 將匯入檔案中的帳戶類型對應到 %{product_name} 的帳戶類型
account_type_mapping_title: 分配您的帳戶類型
- category_mapping_description: 將匯入檔案中的類別對應到 Maybe 現有的類別。您也可以新增類別,或暫時不進行分類。
+ category_mapping_description: 將匯入檔案中的類別對應到 %{product_name} 現有的類別。您也可以新增類別,或暫時不進行分類。
category_mapping_title: 分配您的類別
- tag_mapping_description: 將匯入檔案中的標籤對應到 Maybe 現有的標籤。您也可以新增標籤,或暫時不進行分類。
+ tag_mapping_description: 將匯入檔案中的標籤對應到 %{product_name} 現有的標籤。您也可以新增標籤,或暫時不進行分類。
tag_mapping_title: 分配您的標籤
uploads:
show:
diff --git a/config/locales/views/settings/api_keys/es.yml b/config/locales/views/settings/api_keys/es.yml
index 42f27fc6b..0e9b0c7a6 100644
--- a/config/locales/views/settings/api_keys/es.yml
+++ b/config/locales/views/settings/api_keys/es.yml
@@ -37,7 +37,7 @@ es:
never_expires: "Nunca expira"
permissions: "Permisos"
usage_instructions_title: "Cómo usar tu clave API"
- usage_instructions: "Incluye tu clave API en el encabezado X-Api-Key al realizar solicitudes a la API de Maybe:"
+ usage_instructions: "Incluye tu clave API en el encabezado X-Api-Key al realizar solicitudes a la API de %{product_name}:"
regenerate_key: "Crear Nueva Clave"
revoke_key: "Revocar Clave"
revoke_confirmation: "¿Estás seguro de que deseas revocar esta clave API? Esta acción no se puede deshacer y deshabilitará inmediatamente todas las aplicaciones que usen esta clave."
diff --git a/config/locales/views/settings/api_keys/nb.yml b/config/locales/views/settings/api_keys/nb.yml
index c120ac038..482917d4f 100644
--- a/config/locales/views/settings/api_keys/nb.yml
+++ b/config/locales/views/settings/api_keys/nb.yml
@@ -36,7 +36,7 @@ nb:
never_expires: "Utløper aldri"
permissions: "Tillatelser"
usage_instructions_title: "Hvordan bruke din API-nøkkel"
- usage_instructions: "Inkluder din API-nøkkel i X-Api-Key-headeren når du gjør forespørsler til Maybe API-et:"
+ usage_instructions: "Inkluder din API-nøkkel i X-Api-Key-headeren når du gjør forespørsler til %{product_name} API-et:"
regenerate_key: "Opprett ny nøkkel"
revoke_key: "Tilbakekall nøkkel"
revoke_confirmation: "Er du sikker på at du vil tilbakekalle denne API-nøkkelen? Denne handlingen kan ikke angres og vil umiddelbart deaktivere alle applikasjoner som bruker denne nøkkelen."
diff --git a/config/locales/views/settings/api_keys/pl.yml b/config/locales/views/settings/api_keys/pl.yml
index bef95529c..e5d95264d 100644
--- a/config/locales/views/settings/api_keys/pl.yml
+++ b/config/locales/views/settings/api_keys/pl.yml
@@ -37,7 +37,7 @@ pl:
never_expires: Nigdy nie wygasa
permissions: Uprawnienia
usage_instructions_title: Jak używać klucza API
- usage_instructions: 'Dołącz klucz API w nagłówku X-Api-Key podczas wysyłania żądań do API Maybe:'
+ usage_instructions: 'Dołącz klucz API w nagłówku X-Api-Key podczas wysyłania żądań do API %{product_name}:'
regenerate_key: Utwórz nowy klucz
revoke_key: Unieważnij klucz
revoke_confirmation: Czy na pewno chcesz unieważnić ten klucz API? Tej akcji nie można cofnąć, a wszystkie aplikacje używające tego klucza zostaną natychmiast wyłączone.
diff --git a/config/locales/views/settings/api_keys/tr.yml b/config/locales/views/settings/api_keys/tr.yml
index 9a8457720..9aee48687 100644
--- a/config/locales/views/settings/api_keys/tr.yml
+++ b/config/locales/views/settings/api_keys/tr.yml
@@ -36,7 +36,7 @@ tr:
never_expires: "Süresiz"
permissions: "Yetkiler"
usage_instructions_title: "API anahtarınızı nasıl kullanırsınız"
- usage_instructions: "Maybe API'ye istek yaparken API anahtarınızı X-Api-Key başlığına ekleyin:"
+ usage_instructions: "%{product_name} API'ye istek yaparken API anahtarınızı X-Api-Key başlığına ekleyin:"
regenerate_key: "Yeni Anahtar Oluştur"
revoke_key: "Anahtarı İptal Et"
revoke_confirmation: "Bu API anahtarını iptal etmek istediğinizden emin misiniz? Bu işlem geri alınamaz ve bu anahtarı kullanan tüm uygulamalar hemen devre dışı kalır."
diff --git a/config/locales/views/settings/api_keys/zh-TW.yml b/config/locales/views/settings/api_keys/zh-TW.yml
index 8ec8327ed..7f54ff28d 100644
--- a/config/locales/views/settings/api_keys/zh-TW.yml
+++ b/config/locales/views/settings/api_keys/zh-TW.yml
@@ -37,7 +37,7 @@ zh-TW:
never_expires: "永不過期"
permissions: "權限範圍"
usage_instructions_title: "如何使用您的 API 金鑰"
- usage_instructions: "在向 Maybe API 發送請求時,請在 X-Api-Key 標頭 (Header) 中包含您的 API 金鑰:"
+ usage_instructions: "在向 %{product_name} API 發送請求時,請在 X-Api-Key 標頭 (Header) 中包含您的 API 金鑰:"
regenerate_key: "建立新金鑰"
revoke_key: "撤銷金鑰"
revoke_confirmation: "您確定要撤銷此 API 金鑰嗎?此操作無法還原,且會立即停用所有使用此金鑰的應用程式。"
diff --git a/docs/hosting/ai.md b/docs/hosting/ai.md
index f13f79046..70c89d557 100644
--- a/docs/hosting/ai.md
+++ b/docs/hosting/ai.md
@@ -450,13 +450,18 @@ Pipelock scans for prompt injection, DLP violations, and tool poisoning. The ext
**`NO_PROXY` behavior (Helm/Kubernetes only):** The Helm chart's env template sets `NO_PROXY` to include `.svc.cluster.local` and other internal domains. This means in-cluster agent URLs (like `http://agent.namespace.svc.cluster.local:18789`) bypass the forward proxy and go directly. If your agent is in-cluster, its traffic won't be forward-proxy scanned (but MCP callbacks from the agent are still scanned by the reverse proxy). Docker Compose deployments use a different `NO_PROXY` set; check your compose file for the exact values.
-**`mcpToolPolicy` note:** The Helm chart's `pipelock.mcpToolPolicy.enabled` defaults to `true`. If you haven't defined any policy rules, disable it:
+**`mcpToolPolicy` note:** The Helm chart's `pipelock.mcpToolPolicy.enabled` defaults to `false`. Pipelock rejects an enabled tool policy with no rules, so the chart ships it off by default. To turn it on, define at least one rule and set `enabled: true`:
```yaml
# Helm values
pipelock:
mcpToolPolicy:
- enabled: false
+ enabled: true
+ action: warn
+ rules:
+ - name: example
+ toolPattern: "^shell$"
+ action: block
```
See the [Pipelock documentation](https://github.com/luckyPipewrench/pipelock) for tool policy configuration details.
diff --git a/docs/hosting/mcp.md b/docs/hosting/mcp.md
index 671feb700..db019163f 100644
--- a/docs/hosting/mcp.md
+++ b/docs/hosting/mcp.md
@@ -231,7 +231,7 @@ Pipelock provides:
- **Tool poisoning detection**: Prevents malicious tool call sequences
- **Policy enforcement**: Block or warn on suspicious patterns
-See the [Pipelock documentation](pipelock.md) and the example configuration in `compose.example.pipelock.yml` for setup instructions.
+See the [Pipelock documentation](pipelock.md) and the example configuration in `compose.example.ai.yml` for setup instructions.
### Network Security
diff --git a/docs/hosting/pipelock.md b/docs/hosting/pipelock.md
index 08c14fb13..0cd5ed126 100644
--- a/docs/hosting/pipelock.md
+++ b/docs/hosting/pipelock.md
@@ -77,13 +77,13 @@ Enable Pipelock in your Helm values:
pipelock:
enabled: true
image:
- tag: "2.2.0"
+ tag: "2.5.0"
mode: balanced
```
This creates a separate Deployment, Service, and ConfigMap. The chart auto-injects `HTTPS_PROXY`/`HTTP_PROXY`/`NO_PROXY` into web and worker pods.
-Recent pipelock releases add trusted domain allowlisting, MCP tool redirect profiles, enhanced tool poisoning detection (full JSON schema scanning), per-read kill switch preemption, signed action receipts, per-pattern DLP warn mode, and the `pipelock posture verify` / `pipelock session` CLI commands. See the [pipelock changelog](https://github.com/luckyPipewrench/pipelock/releases) for details.
+Recent pipelock releases add the Audit Packet v0 schema and language-portable verifiers (Go/TypeScript/Rust), request-body prompt-injection blocking, SPIFFE-strict inbound mediation envelopes, scanner attribution on MCP block receipts, trusted domain allowlisting, MCP tool redirect profiles, enhanced tool poisoning detection, per-read kill switch preemption, signed action receipts, per-pattern DLP warn mode, learn-and-lock behavioural contracts, the wedge-detection health watchdog, and the `pipelock posture verify` / `pipelock session` / `pipelock doctor` CLI commands. See the [pipelock changelog](https://github.com/luckyPipewrench/pipelock/releases) for details.
### Exposing MCP to external agents (Kubernetes)
@@ -149,6 +149,7 @@ The `pipelock.example.yaml` file (Docker Compose) or ConfigMap (Helm) controls s
| `trusted_domains` | Allow internal services whose public DNS resolves to private IPs |
| `forward_proxy` | Outbound HTTPS scanning (tunnel timeouts, idle timeouts) |
| `dlp` | Data loss prevention (scan env vars, built-in patterns) |
+| `request_body_scanning` | Scan outbound request bodies for prompt-injection and bodies/sensitive headers for DLP (pipelock 2.5+) |
| `response_scanning` | Scan LLM responses for prompt injection |
| `mcp_input_scanning` | Scan inbound MCP requests |
| `mcp_tool_scanning` | Validate tool calls, detect drift |
@@ -156,6 +157,7 @@ The `pipelock.example.yaml` file (Docker Compose) or ConfigMap (Helm) controls s
| `mcp_session_binding` | Pin tool inventory, detect manipulation |
| `tool_chain_detection` | Multi-step attack patterns |
| `websocket_proxy` | WebSocket frame scanning (disabled by default) |
+| `health_watchdog` | Wedge-detection on subsystem heartbeats, returns 503 on stall (pipelock 2.4+) |
| `logging` | Output format (json/text), verbosity |
For the Helm chart, most sections are configurable via `values.yaml`. For additional sections not covered by structured values (session profiling, data budgets, kill switch, sandbox, reverse proxy, adaptive enforcement), use the `extraConfig` escape hatch:
diff --git a/pipelock.example.yaml b/pipelock.example.yaml
index ee77d162f..4e2af49f9 100644
--- a/pipelock.example.yaml
+++ b/pipelock.example.yaml
@@ -1,12 +1,16 @@
# Pipelock configuration for Docker Compose
# See https://github.com/luckyPipewrench/pipelock for full options.
#
-# Recent additions: trusted_domains, redirect profiles, attack simulation,
-# security scoring, process sandbox, enhanced tool poisoning detection, signed
-# action receipts, per-pattern DLP warn mode, and the `pipelock posture verify`
-# / `pipelock session` CLIs.
+# Recent additions (2.5): Audit Packet v0 schema with Go/TypeScript/Rust
+# verifiers, request-body prompt-injection blocking, SPIFFE-strict inbound
+# mediation envelopes, scanner attribution on MCP block receipts, wedge-
+# detection health watchdog, learn-and-lock behavioural contracts, trusted
+# domains, redirect profiles, attack simulation, security scoring, process
+# sandbox, signed action receipts, per-pattern DLP warn mode, and the
+# `pipelock posture verify` / `pipelock session` / `pipelock doctor` CLIs.
# Run `pipelock simulate --config ` to test your config against 24 attack scenarios.
# Run `pipelock audit score --config ` for a security posture score (0-100).
+# Run `pipelock doctor` to verify configured protections are actually enforceable.
version: 1
mode: balanced
@@ -72,3 +76,23 @@ tool_chain_detection:
action: warn
window_size: 20
max_gap: 3
+
+# Request body scanning (pipelock 2.5+): detect prompt-injection payloads in
+# outbound request bodies (JSON, form-encoded, raw text, WebSocket frames).
+# In enforce mode, prompt-injection findings hard-block non-provider
+# destinations even when action is "warn". Trusted provider hosts (OpenAI,
+# Anthropic, etc.) remain exempt via the response_scanning exemption list.
+request_body_scanning:
+ enabled: false
+ action: warn
+ max_body_bytes: 5242880
+ scan_headers: true
+ header_mode: sensitive
+
+# Health watchdog (pipelock 2.4+): /health returns 503 when any subsystem
+# heartbeat goes stale. Enabled by default; set expose_subsystems true to
+# include a per-subsystem boolean map in /health responses.
+health_watchdog:
+ enabled: true
+ interval_seconds: 2
+ expose_subsystems: false
diff --git a/test/controllers/enable_banking_items_controller_test.rb b/test/controllers/enable_banking_items_controller_test.rb
new file mode 100644
index 000000000..06005bc9a
--- /dev/null
+++ b/test/controllers/enable_banking_items_controller_test.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require "test_helper"
+require "openssl"
+
+class EnableBankingItemsControllerTest < ActionDispatch::IntegrationTest
+ setup do
+ sign_in users(:family_admin)
+ @family = families(:dylan_family)
+ @item = @family.enable_banking_items.create!(
+ name: "Test Connection",
+ country_code: "DE",
+ application_id: "test_app_id",
+ client_certificate: OpenSSL::PKey::RSA.new(2048).to_pem
+ )
+ end
+
+ test "select_bank exposes ASPSP BIC in the searchable data attribute" do
+ Provider::EnableBanking.any_instance.stubs(:get_aspsps).returns(
+ aspsps: [
+ {
+ name: "ING-DiBa AG",
+ country: "DE",
+ bic: "INGDDEFF",
+ beta: false,
+ psu_types: [ "personal" ],
+ auth_methods: [ { approach: "REDIRECT" } ]
+ }
+ ]
+ )
+
+ get select_bank_enable_banking_item_url(@item)
+
+ assert_response :success
+ haystack = @response.body[/data-bank-search="([^"]*)"/, 1]
+ assert haystack, "Expected list items to render a data-bank-search attribute the client filter reads from"
+ assert_includes haystack, "ingddeff",
+ "Expected the searchable data attribute to include the BIC so users can find banks by BIC code"
+ assert_includes haystack, "ing-diba ag",
+ "Expected the searchable data attribute to still include the bank name (existing name-search behavior)"
+ end
+end
diff --git a/test/controllers/settings/hostings_controller_test.rb b/test/controllers/settings/hostings_controller_test.rb
index 4ebe20f87..e4ecca9bb 100644
--- a/test/controllers/settings/hostings_controller_test.rb
+++ b/test/controllers/settings/hostings_controller_test.rb
@@ -95,6 +95,55 @@ class Settings::HostingsControllerTest < ActionDispatch::IntegrationTest
end
end
+ # Regression: issue #1824. The OpenAI form auto-submits on blur, so entering
+ # the URI base before the model fires a partial submit that fails validation.
+ # The re-rendered form must show the user's submitted URI base — not the
+ # still-blank saved value — so they can finish typing the model.
+ test "preserves submitted openai uri base in form when validation fails" do
+ with_self_hosting do
+ Setting.openai_uri_base = nil
+ Setting.openai_model = ""
+
+ patch settings_hosting_url, params: { setting: { openai_uri_base: "https://api.example.com/v1" } }
+
+ assert_response :unprocessable_entity
+ assert_select "input[name=?]", "setting[openai_uri_base]" do |inputs|
+ assert_equal "https://api.example.com/v1", inputs.first["value"]
+ end
+ end
+ ensure
+ Setting.openai_uri_base = nil
+ Setting.openai_model = nil
+ end
+
+ # PR #1862 review (jjmata): symmetric coverage for the model field. When the
+ # user changes the URI base and clears the model in the same auto-submit, the
+ # cross-field validation fails — the re-rendered model input must reflect the
+ # user's submitted (cleared) value, not silently revert to the saved model.
+ test "preserves submitted openai model in form when validation fails" do
+ with_self_hosting do
+ Setting.openai_uri_base = "https://saved.example.com/v1"
+ Setting.openai_model = "saved-model"
+
+ patch settings_hosting_url, params: { setting: {
+ openai_uri_base: "https://new.example.com/v1",
+ openai_model: ""
+ } }
+
+ assert_response :unprocessable_entity
+ assert_select "input[name=?]", "setting[openai_uri_base]" do |inputs|
+ assert_equal "https://new.example.com/v1", inputs.first["value"]
+ end
+ assert_select "input[name=?]", "setting[openai_model]" do |inputs|
+ assert_not_equal "saved-model", inputs.first["value"].to_s,
+ "model field must reflect the submitted (cleared) value, not the saved model"
+ end
+ end
+ ensure
+ Setting.openai_uri_base = nil
+ Setting.openai_model = nil
+ end
+
test "can update openai model alone when self hosting is enabled" do
with_self_hosting do
patch settings_hosting_url, params: { setting: { openai_model: "gpt-4" } }
diff --git a/test/models/account_test.rb b/test/models/account_test.rb
index 6b1507159..de44d90cb 100644
--- a/test/models/account_test.rb
+++ b/test/models/account_test.rb
@@ -382,4 +382,41 @@ class AccountTest < ActiveSupport::TestCase
assert_equal [ provider_holding.id, second_provider_holding.id ].sort, account.current_holdings.pluck(:id).sort
assert_equal %w[CHF EUR], account.current_holdings.pluck(:currency).sort
end
+
+ test "on account destroyed cascade transfer destroyed" do
+ outflow_account = @family.accounts.create!({
+ owner: @admin,
+ name: "test_account_outflow",
+ balance: 100,
+ currency: "USD",
+ accountable_type: "Depository",
+ accountable_attributes: {}
+ })
+ inflow_account = @family.accounts.create!({
+ owner: @admin,
+ name: "test_account_inflow",
+ balance: 100,
+ currency: "USD",
+ accountable_type: "Depository",
+ accountable_attributes: {}
+ })
+
+ transfer = create_transfer(
+ from_account: outflow_account,
+ to_account: inflow_account,
+ amount: 50
+ )
+
+ outflow_transaction = transfer.outflow_transaction
+
+ outflow_transaction.reload
+ assert_equal "funds_movement", outflow_transaction.kind
+
+ inflow_account.destroy!
+
+ assert_raises(ActiveRecord::RecordNotFound) { transfer.reload }
+
+ outflow_transaction.reload
+ assert_equal "standard", outflow_transaction.kind
+ end
end
diff --git a/test/models/ibkr_account/historical_balances_sync_test.rb b/test/models/ibkr_account/historical_balances_sync_test.rb
index 66b5ee89b..825cc1aaf 100644
--- a/test/models/ibkr_account/historical_balances_sync_test.rb
+++ b/test/models/ibkr_account/historical_balances_sync_test.rb
@@ -213,6 +213,163 @@ class IbkrAccount::HistoricalBalancesSyncTest < ActiveSupport::TestCase
assert_not_nil @account.balances.find_by(date: Date.new(2026, 5, 8), currency: "CHF")
end
+ test "computes net_market_flows from equity delta minus trade flows" do
+ # Day 1: total=3000, cash=500, non_cash=2500
+ # Day 2: total=3200, cash=500, non_cash=2700 (Δnon_cash=200)
+ # Buy trade on Day 2: CHF 150 (same currency as account, no FX)
+ # nmf = 200 - 150 = 50
+ @ibkr_account.update!(
+ raw_equity_summary_payload: [
+ { report_date: "2026-05-07", total: "3000.00" },
+ { report_date: "2026-05-08", total: "3200.00" }
+ ]
+ )
+ seed_balance(date: Date.new(2026, 5, 7), balance: 3000.00, cash_balance: 500.00)
+ seed_balance(date: Date.new(2026, 5, 8), balance: 3200.00, cash_balance: 500.00)
+
+ security = Security.create!(ticker: "TEST", name: "Test Stock")
+ @account.entries.create!(
+ name: "Buy 100 TEST",
+ date: Date.new(2026, 5, 8),
+ amount: 150.00,
+ currency: "CHF",
+ entryable: Trade.new(security: security, qty: 100, price: 1.5, currency: "CHF")
+ )
+
+ IbkrAccount::HistoricalBalancesSync.new(@ibkr_account).sync!
+
+ day1 = @account.balances.find_by!(date: Date.new(2026, 5, 7), currency: "CHF")
+ day2 = @account.balances.find_by!(date: Date.new(2026, 5, 8), currency: "CHF")
+
+ assert_equal BigDecimal("0"), day1.net_market_flows
+ assert_equal BigDecimal("50"), day2.net_market_flows
+
+ # Virtual column must still resolve to IBKR's equity total minus cash
+ assert_equal BigDecimal("2500.00"), day1.end_non_cash_balance
+ assert_equal BigDecimal("2700.00"), day2.end_non_cash_balance
+ end
+
+ test "applies fx_rate_to_base when trade currency differs from account currency" do
+ # Trade in EUR with fx_rate_to_base=1.1 → CHF 165, not CHF 150
+ # Day 1: non_cash=2500, Day 2: non_cash=2700 (Δ=200)
+ # nmf = 200 - 165 = 35
+ @ibkr_account.update!(
+ raw_equity_summary_payload: [
+ { report_date: "2026-05-07", total: "3000.00" },
+ { report_date: "2026-05-08", total: "3200.00" }
+ ]
+ )
+ seed_balance(date: Date.new(2026, 5, 7), balance: 3000.00, cash_balance: 500.00)
+ seed_balance(date: Date.new(2026, 5, 8), balance: 3200.00, cash_balance: 500.00)
+
+ security = Security.create!(ticker: "TEST2", name: "Test Stock EUR")
+ trade = Trade.new(security: security, qty: 100, price: 1.5, currency: "EUR")
+ trade.exchange_rate = 1.1
+ @account.entries.create!(
+ name: "Buy 100 TEST2",
+ date: Date.new(2026, 5, 8),
+ amount: 150.00,
+ currency: "EUR",
+ entryable: trade
+ )
+
+ IbkrAccount::HistoricalBalancesSync.new(@ibkr_account).sync!
+
+ day2 = @account.balances.find_by!(date: Date.new(2026, 5, 8), currency: "CHF")
+ assert_in_delta 35.0, day2.net_market_flows.to_f, 0.01
+ assert_equal BigDecimal("2700.00"), day2.end_non_cash_balance
+ end
+
+ test "excludes balance row from upsert when Money::ConversionError prevents FX conversion" do
+ # EUR trade with no exchange_rate stored → custom_rate=nil → ConversionError raised.
+ # The affected date is excluded from the upsert entirely so net_market_flows is not
+ # silently wrong (the trade's value would otherwise flow into market appreciation).
+ # The seeded day2 balance is intentionally different from IBKR's total (3150 vs 3200)
+ # so we can assert the row was not overwritten by sync.
+ @ibkr_account.update!(
+ raw_equity_summary_payload: [
+ { report_date: "2026-05-07", total: "3000.00" },
+ { report_date: "2026-05-08", total: "3200.00" }
+ ]
+ )
+ seed_balance(date: Date.new(2026, 5, 7), balance: 3000.00, cash_balance: 500.00)
+ seed_balance(date: Date.new(2026, 5, 8), balance: 3150.00, cash_balance: 500.00)
+
+ security = Security.create!(ticker: "NORATE", name: "No Rate EUR Stock")
+ @account.entries.create!(
+ name: "Buy 100 NORATE",
+ date: Date.new(2026, 5, 8),
+ amount: 150.00,
+ currency: "EUR",
+ entryable: Trade.new(security: security, qty: 100, price: 1.5, currency: "EUR")
+ )
+
+ Money.any_instance.stubs(:exchange_to).raises(
+ Money::ConversionError.new(from_currency: "EUR", to_currency: "CHF", date: Date.new(2026, 5, 8))
+ )
+
+ IbkrAccount::HistoricalBalancesSync.new(@ibkr_account).sync!
+
+ # Day 1 is unaffected — still synced normally
+ day1 = @account.balances.find_by!(date: Date.new(2026, 5, 7), currency: "CHF")
+ assert_equal BigDecimal("3000.00"), day1.balance
+
+ # Day 2 was excluded from the upsert — seeded values are preserved, not overwritten
+ day2 = @account.balances.find_by!(date: Date.new(2026, 5, 8), currency: "CHF")
+ assert_equal BigDecimal("3150.00"), day2.balance # seeded, not IBKR's 3200
+ assert_equal BigDecimal("0"), day2.net_market_flows # seeded, not recomputed
+ end
+
+ test "net_market_flows equals full non_cash delta when account has no trades" do
+ @ibkr_account.update!(
+ raw_equity_summary_payload: [
+ { report_date: "2026-05-07", total: "3000.00" },
+ { report_date: "2026-05-08", total: "3300.00" }
+ ]
+ )
+ seed_balance(date: Date.new(2026, 5, 7), balance: 3000.00, cash_balance: 500.00)
+ seed_balance(date: Date.new(2026, 5, 8), balance: 3300.00, cash_balance: 500.00)
+
+ IbkrAccount::HistoricalBalancesSync.new(@ibkr_account).sync!
+
+ day1 = @account.balances.find_by!(date: Date.new(2026, 5, 7), currency: "CHF")
+ day2 = @account.balances.find_by!(date: Date.new(2026, 5, 8), currency: "CHF")
+
+ assert_equal BigDecimal("0"), day1.net_market_flows
+ assert_equal BigDecimal("300"), day2.net_market_flows
+ assert_equal BigDecimal("2800.00"), day2.end_non_cash_balance
+ end
+
+ test "sell trades reduce net_buy_sell so market loss is isolated in net_market_flows" do
+ # Day 1: total=3000, cash=500, non_cash=2500
+ # Day 2: total=2700, cash=700, non_cash=2000 (Δnon_cash=-500)
+ # Sell 100 at CHF 1.50: entry.amount=-150 (negative = proceeds received)
+ # net_buy_sell=-150; nmf = -500 - (-150) = -350 (market caused -350 loss)
+ @ibkr_account.update!(
+ raw_equity_summary_payload: [
+ { report_date: "2026-05-07", total: "3000.00" },
+ { report_date: "2026-05-08", total: "2700.00" }
+ ]
+ )
+ seed_balance(date: Date.new(2026, 5, 7), balance: 3000.00, cash_balance: 500.00)
+ seed_balance(date: Date.new(2026, 5, 8), balance: 2700.00, cash_balance: 700.00)
+
+ security = Security.create!(ticker: "SELL_TEST", name: "Sell Test Stock")
+ @account.entries.create!(
+ name: "Sell 100 SELL_TEST",
+ date: Date.new(2026, 5, 8),
+ amount: -150.00,
+ currency: "CHF",
+ entryable: Trade.new(security: security, qty: -100, price: 1.5, currency: "CHF")
+ )
+
+ IbkrAccount::HistoricalBalancesSync.new(@ibkr_account).sync!
+
+ day2 = @account.balances.find_by!(date: Date.new(2026, 5, 8), currency: "CHF")
+ assert_equal BigDecimal("-350"), day2.net_market_flows
+ assert_equal BigDecimal("2000.00"), day2.end_non_cash_balance
+ end
+
test "writes balance row with zero total for fully liquidated dates" do
@ibkr_account.update!(
raw_equity_summary_payload: [