---
url: /en/platform/getting-started/quick-start.md
---
# Quick Start Guide
::: tip Automate with sp CLI
For Java record-and-replay testing (agent, policies, replay), see [Testing getting started](/en/testing/getting-started). To automate with `sp`, see [CLI quickstart](/en/testing/getting-started).
:::
::: info Important
The Quick Start demo environment already has the SESSIFY (`@softprobe/sessify`) pre-installed and enabled, so you don't need to install it again.
If you want to integrate the SDK into your own frontend app, see [SESSIFY Integration](/en/platform/sessify).
:::
Get started with SP-Istio Agent in minutes using a local Kubernetes cluster with Kind.
::: info Time Estimate
* Setup: 10-15 minutes
* Demo exploration: 5-10 minutes
:::
## Prerequisites
* **Operating System**: macOS (or Linux with Docker)
* **Required Tools**:
* [Docker Desktop](https://www.docker.com/products/docker-desktop)
* [Kind](https://kind.sigs.k8s.io/) - `brew install kind`
* [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl-macos/) - `brew install kubectl`
* [Istio CLI](https://istio.io/latest/docs/setup/getting-started/#download) - `brew install istioctl`
### Install All Tools at Once
```bash
brew install kind kubectl istioctl
```
## Step 1: Set up Kind Cluster with Istio
Create a Kind cluster and install Istio with OpenTelemetry Operator:
```bash
curl -L https://raw.githubusercontent.com/softprobe/softprobe/main/scripts/cluster-setup.sh | sh
```
::: tip
This script will automatically:
* Create a local Kubernetes cluster using Kind
* Install Istio service mesh
* Install OpenTelemetry Operator for telemetry collection
:::
## Step 2: Install the Travel Demo
Deploy the demo application with SP-Istio Agent:
::: info Important
Use the `minimal.yaml` file you downloaded from the [Account Setup](/en/platform/getting-started/account-setup) guide.
:::
```bash
# Install Softprobe Istio WASM Plugin (using the minimal.yaml downloaded from Account Setup)
kubectl apply -f minimal.yaml
# Install demo app
kubectl apply -f https://raw.githubusercontent.com/softprobe/softprobe/main/examples/travel/apps.yaml
# Expose the demo
sleep 10 && kubectl port-forward -n istio-system svc/istio-ingressgateway 8080:80
```
## Step 3: Try the Demo
1. Open [`http://localhost:8080/`](http://localhost:8080/) in your browser
2. Select a **pair** of cities
5) Process a payment with fake details
::: tip
Generate at least 5-10 bookings to see meaningful data in the dashboard.
:::
## Step 4: View Results in Softprobe Dashboard
After generating some traffic:
1. Go to [Softprobe Dashboard](https://dashboard.softprobe.ai)
2. Navigate to **Travel View** in the left navigation menu
3. Explore the captured requests and business-level traces
### Demo Video
## Cleanup
When you're done with the demo, clean up the Kind cluster:
```bash
kind delete cluster --name sp-demo-cluster
```
::: tip Congratulations!
You've successfully set up Softprobe and seen it in action! Next steps:
* [Production Deployment](/en/platform/deployment/installation) - Deploy to your production cluster
* [Configuration Guide](/en/platform/configuration/config) - Customize collection rules
* [Advanced Concepts](/en/platform/advanced-guides/concepts) - Learn how Softprobe works
:::
---
---
url: /en/platform/getting-started/account-setup.md
---
# Account Setup & Public Key Management
Set up your Softprobe account and manage public keys for secure authentication.
::: info Prerequisites
* A valid email address
* Access to the Softprobe Dashboard
:::
## Overview
This guide walks you through:
* Creating a Softprobe account
* Setting up your tenant group
* Generating and managing public keys
* Downloading configuration files
### Step 1: Create Your Account
1. Visit [Softprobe Dashboard](https://dashboard.softprobe.ai)
2. Click **"Sign Up"** to create a new account
3. Fill in your details:
* **Email address** (will be your login username)
* **Password** (minimum 8 characters)
4. Verify your email address by clicking the link sent to your inbox
### Step 2: Access Settings and Create Tenant Groups
After email verification and login, you can access the Settings page to manage tenant groups:
1. Navigate to the **[Settings](https://dashboard.softprobe.ai/settings)** page
2. **Create your first tenant group**
* Click the "Create Group" button
* **Tenant Group Name**: Choose a unique name for your organization
* This will be used to organize your services and data
* Example: `my-company-prod`, `acme-corp`, `team-alpha`
* **Description** (optional): Add a brief description of your group
3. Click **"Create Tenant Group"**
::: tip
Choose your tenant group name carefully as it cannot be changed later. Use a name that clearly identifies your organization or team.
:::
### Step 3: Generate Public Key
Once your tenant group is created:
1. Navigate to **"Public Keys"** in the dashboard sidebar
2. Click **"Generate New Public Key"**
3. Provide the following information:
* **Key Name**: A descriptive name (e.g., `production-cluster`, `dev-environment`)
* **Environment**: Select the appropriate environment type
4. Click **"Generate Key"**
:::caution Important
Softprobe uses **public key authentication** instead of traditional API keys. This means:
* Your configuration contains only a public identifier, not a secret
* No sensitive credentials are stored in your Kubernetes configuration
* Public keys can be easily rotated without service disruption
* Uses asymmetric cryptography for authentication
:::
## 📁 Configuration File
When you generate a public key, a `minimal.yaml` file is automatically downloaded. This file contains:
* Your public key identifier (not a secret)
* Pre-configured endpoints
* Default collection rules
* All necessary Kubernetes resources
### File Structure
The downloaded `minimal.yaml` includes:
```yaml
# WasmPlugin configuration with your public key
apiVersion: extensions.istio.io/v1alpha1
kind: WasmPlugin
metadata:
name: sp-istio-agent
spec:
pluginConfig:
public_key: "your-public-key-identifier"
# ... other secure configurations
```
After completing account setup:
1. **For Quick Testing**: Follow the [Quick Start Guide](/en/platform/getting-started/quick-start)
2. **For Production**: Follow the [Production Installation Guide](/en/platform/deployment/installation)
3. **For Custom Configuration**: Review the [Configuration Reference](/en/platform/configuration/config)
## ❓ Troubleshooting
### Common Issues
**Can't access the dashboard?**
* Check your internet connection
* Verify the URL: `https://dashboard.softprobe.ai`
* Try clearing your browser cache
**Public key generation failed?**
* Ensure your tenant group is properly set up
* Check that you have the necessary permissions
* Try refreshing the page and generating again
***
## FAQ
> Common questions have been moved to a separate page:
>
> * [Getting Started FAQ](/en/platform/support/faq)
---
---
url: /en/platform/deployment/installation.md
---
# Server-side Agent (Istio WASM) & SESSIFY Installation
Deploy SP‑Istio Agent to your Istio service mesh, and integrate the SESSIFY for client-side enrichment.
::: tip Looking for user install?
Softprobe server install (Helm sp-backend), client install (`sp setup`, Spcode Service), `sp code`, `sp doctor`, and `sp upgrade` are documented under [Testing installation](/en/testing/installation/). This Platform page is for Istio mesh / SESSIFY deployment.
:::
## Prerequisites
Before installing SP-Istio Agent in production, ensure you have:
* A running Kubernetes cluster
* Istio installed and configured
* kubectl access with appropriate permissions
* Network connectivity to Softprobe endpoints
::: info Using GKE Autopilot?
If your cluster runs on GKE Autopilot, be aware of these common installation/permission constraints (summary from the full guide):
* NET\_ADMIN capability is disabled by default, which can break istio-init/iptables steps
* You cannot modify the CNI ConfigMap in the kube-system namespace (managed namespace restrictions)
* Some system namespaces are managed/protected and certain resources cannot be changed
Quick fixes:
* Enable workload policies when creating or updating the cluster: `--workload-policies=allow-net-admin`
* Disable the Istio CNI component during installation: `--set components.cni.enabled=false`
Read the full step-by-step guide, verification, and troubleshooting:
[GKE Autopilot Istio Installation Guide →](./GKE-Autopilot-Istio-Installation-Guide)
:::
## Install SESSIFY (Client-Side Enrichment)
Add the Softprobe SESSIFY to your frontend to create session-scoped context and capture route changes. This provides full-context visibility without modifying server-side code.
### Install package
```bash
npm install @softprobe/sessify
```
### Initialize at Your App Entry Point
```jsx
// app/layout.tsx or your application's entry file
'use client'
import { useEffect } from 'react';
import { initSessify } from '@softprobe/sessify';
export default function RootLayout({ children }) {
useEffect(() => {
// Initialize the session management library
// This is the necessary first step to use @softprobe/sessify
initSessify({});
}, []);
return (
{children}
);
}
```
See the full [Sessify guide](/en/platform/sessify) for framework-specific examples (React/Vue/Next.js) and advanced usage.
## Install Server-side Agent (Istio WasmPlugin)
Install SP‑Istio Agent using your personalized `minimal.yaml` file downloaded during [Account Setup](/en/platform/getting-started/account-setup). It contains your public key identifier and pre-configured settings.
```bash
# Use the minimal.yaml file downloaded from the Softprobe Dashboard
kubectl apply -f minimal.yaml
```
::: tip Namespace
If your `minimal.yaml` does not set `metadata.namespace`, specify a namespace explicitly:
```bash
kubectl apply -n -f minimal.yaml
```
Or set it in the YAML:
```yaml
metadata:
namespace:
```
Choose the namespace that matches where Istio is installed and where you manage mesh‑wide resources in your cluster.
:::
This deploys the WasmPlugin globally across your Istio service mesh.
## Verify Installation
Check that the WasmPlugin has been created successfully:
```bash
kubectl get wasmplugin -A
```
You should see the SP-Istio Agent plugin listed.
## Restart Workloads
After applying the WasmPlugin/EnvoyFilter, restart affected workloads to load the updated sidecar configuration:
```bash
# Restart all deployments in a namespace (replace )
kubectl rollout restart deployment -n
# Or restart a single deployment
kubectl rollout restart deployment -n
```
## View Context View in Dashboard
If you enabled sidecar injection on a namespace just now, restarting ensures pods are recreated with the updated sidecar and configuration.
::: tip Next: View Context View in Dashboard
After deploying SP‑Istio Agent and initializing the SESSIFY, generate some traffic in your app, then:
1. Open your Softprobe Dashboard → Context View
2. Select the time range and environment (env) matching your deployment
3. Filter by serviceName if needed; search by userId/sessionId/request\_body\_hash to locate sessions
4. Click a session to inspect the end‑to‑end graph, spans, client metrics, and interaction events
You do not need to change server‑side code to get full‑context visibility.
:::
## Configuration
Softprobe’s default configuration captures HTTP traffic for all services in the mesh. To customize the capture scope, service identification, and advanced options, please refer to the full Configuration Guide: [Configuration Guide](/en/platform/configuration/config). You can start from the minimal example and gradually extend `collectionRules`, service discovery, and external communication settings based on your needs.
### Scoped Deployment
To deploy the agent to specific namespaces or workloads only, you can create a scoped WasmPlugin configuration. See the [Configuration Guide](/en/platform/configuration/config) for detailed configuration options.
---
---
url: /en/platform/deployment/GKE-Autopilot-Istio-Installation-Guide.md
---
# Complete Guide to Installing Istio on GKE Autopilot
## Overview
This guide provides detailed instructions on how to successfully install and configure Istio service mesh on Google Kubernetes Engine (GKE) Autopilot clusters. GKE Autopilot is Google's managed Kubernetes service that offers hardened defaults and a simplified management experience.
## Prerequisites
### System Requirements
* **GKE Cluster Version**: 1.27 or higher
* **gcloud CLI**: Installed and configured
* **kubectl**: Installed and configured
* **istioctl**: Istio command-line tool
### Permission Requirements
* Administrator access to the GKE cluster
* Permission to modify cluster configurations
## Core Issues and Solutions
### Problem Background
The main challenges when installing Istio on GKE Autopilot are:
1. **NET\_ADMIN Permission Restrictions**: Autopilot disables `NET_ADMIN` Linux capability by default as part of hardened defaults
2. **CNI Component Limitations**: Cannot modify ConfigMaps in the `kube-system` namespace
3. **Managed Namespace Restrictions**: Certain system namespaces are managed and protected by Google
### Key Solution
**Enabling NET\_ADMIN capability** is the key to solving Istio installation issues!
## Detailed Installation Steps
### Step 1: Check Cluster Version
```bash
# Check Kubernetes version
kubectl version --short
# Check cluster information
kubectl get nodes -o wide
```
Ensure the cluster version is 1.27 or higher.
### Step 2: Configure gcloud Project
```bash
# Set the correct project ID
gcloud config set project YOUR_PROJECT_ID
# Verify configuration
gcloud config list
```
### Step 3: Enable NET\_ADMIN Capability for Cluster
#### New Cluster Creation (Recommended)
```bash
gcloud container clusters create-auto istio-cluster \
--region=us-central1 \
--workload-policies=allow-net-admin \
--cluster-version=1.27.2-gke.1200
```
#### Existing Cluster Update
```bash
gcloud container clusters update CLUSTER_NAME \
--region=REGION \
--workload-policies=allow-net-admin
```
**Important Note**: The `--workload-policies=allow-net-admin` parameter is crucial for successful Istio installation!
### Step 4: Install Istio
#### 4.1 Download Istio
```bash
# Download latest version
curl -L https://istio.io/downloadIstio | sh -
# Or download specific version
export ISTIO_VERSION=1.27.1
curl -L https://istio.io/downloadIstio | TARGET_ARCH=$(uname -m) sh -
# Add to PATH
cd istio-*
export PATH=$PWD/bin:$PATH
```
#### 4.2 Install Istio Control Plane
```bash
# Use default profile with CNI component disabled
istioctl install --set profile=default --set components.cni.enabled=false -y
```
**Key Configuration Explanation**:
* `--set profile=default`: Uses production-recommended configuration
* `--set components.cni.enabled=false`: Disables CNI component to avoid kube-system permission issues
### Step 5: Verify Installation
#### 5.1 Check Istio Component Status
```bash
# Check Istio system components
kubectl get pods -n istio-system
# Expected output:
# NAME READY STATUS RESTARTS AGE
# istio-ingressgateway-xxx 1/1 Running 0 2m
# istiod-xxx 1/1 Running 0 2m
```
#### 5.2 Verify CRD Installation
```bash
# Check Istio CRDs
kubectl get crd | grep istio
# Should see the following CRDs:
# - wasmplugins.extensions.istio.io
# - serviceentries.networking.istio.io
# - destinationrules.networking.istio.io
# - envoyfilters.networking.istio.io
# - etc...
```
### Step 6: Configure Namespaces
#### 6.1 Enable Sidecar Injection
```bash
# Enable automatic sidecar injection for target namespace
kubectl label namespace YOUR_NAMESPACE istio-injection=enabled
# Verify label
kubectl describe namespace YOUR_NAMESPACE
```
#### 6.2 Apply Istio Configuration
```bash
# Apply your Istio resource configuration
kubectl apply -f your-istio-config.yaml
```
## Common Issues and Solutions
### Issue 1: NET\_ADMIN Permission Denied
**Error Message**:
```
linux capability 'NET_ADMIN' on container 'istio-init' not allowed
```
**Solution**:
Ensure `--workload-policies=allow-net-admin` is enabled
### Issue 2: CNI Installation Failure
**Error Message**:
```
failed to update resource with server-side apply for obj ConfigMap/kube-system/istio-cni-config
```
**Solution**:
Use `--set components.cni.enabled=false` to disable CNI component
### Issue 3: Project Permission Issues
**Error Message**:
```
Kubernetes Engine API has not been used in project
```
**Solution**:
Ensure gcloud configuration points to the correct project ID
## Best Practices
### Performance Optimization
1. **Resource Limits**: Set appropriate resource limits for sidecar containers
2. **Monitoring**: Deploy Istio monitoring components (Prometheus, Grafana, Jaeger)
3. **Log Management**: Configure appropriate log levels and rotation policies
### Maintenance Recommendations
1. **Regular Updates**: Keep Istio versions up to date
2. **Configuration Backup**: Regularly backup Istio configurations
3. **Test Environment**: Validate in test environment before production
## Deployment Verification
### Deploy Test Application
```bash
# Deploy sample application to verify Istio functionality
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.27/samples/bookinfo/platform/kube/bookinfo.yaml
# Check sidecar injection
kubectl get pods -o="custom-columns=NAME:.metadata.name,CONTAINERS:.spec.containers[*].name"
```
### Test Traffic Management
```bash
# Create Gateway and VirtualService
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.27/samples/bookinfo/networking/bookinfo-gateway.yaml
# Get Ingress Gateway address
kubectl get svc istio-ingressgateway -n istio-system
```
## Summary
Key points for successfully installing Istio on GKE Autopilot:
1. ✅ **Enable NET\_ADMIN capability**: This is the most important step
2. ✅ **Use correct configuration**: Disable CNI component to avoid permission issues
3. ✅ **Verify installation**: Ensure all components are running properly
4. ✅ **Configure namespaces**: Enable sidecar injection
By following this guide, you should be able to successfully deploy and run Istio service mesh on GKE Autopilot clusters.
## References
* [Istio Official Documentation](https://istio.io/latest/docs/)
* [GKE Autopilot Documentation](https://cloud.google.com/kubernetes-engine/docs/concepts/autopilot-overview)
* [GKE Autopilot Hardened Defaults (Security)](https://cloud.google.com/kubernetes-engine/docs/concepts/autopilot-security)
*This guide is based on actual deployment experience and is applicable to Istio 1.27+ and GKE 1.27+ versions.*
## Application Instrumentation on Autopilot (without Operator)
In some GKE Autopilot environments, installing cluster-wide operators (like OpenTelemetry Operator) can be constrained by security policies, private cluster firewall rules, or webhook requirements. If you can’t (or prefer not to) use the Operator, you can manually attach language-specific agents to your applications and export telemetry to an OTLP endpoint (Collector or Softprobe ingestion endpoint).
### General setup
* Choose an OTLP endpoint (Collector service or external ingestion URL)
* Set service metadata via environment variables
* Ensure egress from workloads to the OTLP endpoint (HTTP or gRPC)
* Prefer non-root containers and define resource requests/limits to comply with Autopilot
Common environment variables (adapt to your endpoint):
```bash
# Example (HTTP OTLP)
export OTEL_EXPORTER_OTLP_ENDPOINT="https://otel.example.com" # base URL, the SDK will append /v1/traces /v1/metrics
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf" # or "grpc"
export OTEL_SERVICE_NAME="your-service"
export OTEL_RESOURCE_ATTRIBUTES="service.namespace=production,service.version=1.0.0"
# Optional headers (e.g., auth token)
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer YOUR_TOKEN"
```
You may set these variables directly in your Kubernetes Deployment under `env:`.
***
### Java (JVM)
Attach the OpenTelemetry Java agent by adding `-javaagent` and environment variables:
```dockerfile
# Add the agent to the image (recommendation: bake into your app image)
ADD opentelemetry-javaagent.jar /otel/javaagent.jar
```
```yaml
# Deployment snippet
spec:
template:
spec:
containers:
- name: app
image: your-registry/your-java-app:latest
env:
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "https://otel.example.com"
- name: OTEL_EXPORTER_OTLP_PROTOCOL
value: "http/protobuf"
- name: OTEL_SERVICE_NAME
value: "your-service"
- name: OTEL_RESOURCE_ATTRIBUTES
value: "service.namespace=production,service.version=1.0.0"
- name: OTEL_EXPORTER_OTLP_HEADERS
value: "Authorization=Bearer YOUR_TOKEN"
- name: JAVA_TOOL_OPTIONS
value: "-javaagent:/otel/javaagent.jar"
# or use command/args if you manage the JVM startup explicitly
```
If you control the startup script, you can also add: `-javaagent:/otel/javaagent.jar` to the JVM arguments.
***
### Node.js
Use the Node SDK and auto-instrumentations:
```bash
npm install @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node @opentelemetry/exporter-trace-otlp-http
```
Create a bootstrap file (e.g., `otel.js`):
```js
// otel.js
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
const exporter = new OTLPTraceExporter({
// The OTLP exporter appends /v1/traces automatically for HTTP
url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
headers: process.env.OTEL_EXPORTER_OTLP_HEADERS
? Object.fromEntries(process.env.OTEL_EXPORTER_OTLP_HEADERS.split(',').map(h => h.split('=')))
: undefined,
});
const sdk = new NodeSDK({
traceExporter: exporter,
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
```
Start your app with the bootstrap required:
```bash
# Option 1: require bootstrap
node -r ./otel.js app.js
# Option 2: via NODE_OPTIONS
export NODE_OPTIONS="--require ./otel.js" && node app.js
```
Set env variables in your Deployment as shown in the General setup section.
***
### Python
Use the Python distro and the CLI instrumentation:
```bash
pip install opentelemetry-distro opentelemetry-exporter-otlp
opentelemetry-bootstrap --action=install
```
Run the application with instrumentation:
```bash
# Set env vars (as in General setup) then
opentelemetry-instrument python app.py
```
Alternatively, configure the SDK in code and use OTLP exporters.
***
### .NET
For .NET, you can use SDK-based instrumentation or auto-instrumentation (native profiler). SDK-based is simpler to adopt:
```csharp
// Program.cs (example)
using OpenTelemetry;
using OpenTelemetry.Trace;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenTelemetry().WithTracing(tracerProviderBuilder =>
{
tracerProviderBuilder
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddOtlpExporter(options =>
{
options.Endpoint = new Uri(Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "https://otel.example.com");
// For HTTP/protobuf, ensure protocol matches; set headers if needed
});
});
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
```
If you need auto-instrumentation, mount the auto-instrumentation files and set the profiler env vars (`CORECLR_ENABLE_PROFILING`, `CORECLR_PROFILER`, `CORECLR_PROFILER_PATH`, and relevant `OTEL_*` variables) in the Deployment.
***
### Go
Go commonly uses SDK-based instrumentation in code:
```go
// Example outline
import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/sdk/resource"
"go.opentelemetry.io/otel/sdk/trace"
)
func initTracer() (*trace.TracerProvider, error) {
exporter, err := otlptracehttp.New(context.Background(), otlptracehttp.WithEndpoint(os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")))
if err != nil { return nil, err }
tp := trace.NewTracerProvider(
trace.WithBatcher(exporter),
trace.WithResource(resource.Default()),
)
otel.SetTracerProvider(tp)
return tp, nil
}
```
For eBPF-based HTTP telemetry in Go environments without code changes, consider a separate DaemonSet like Beyla. In Autopilot, ensure it complies with non-privileged policies.
***
### Troubleshooting on Autopilot
* Verify egress to the OTLP endpoint and TLS/cert requirements
* Define resource requests/limits for all containers
* Avoid privileged flags and root-only file paths
* Prefer baking agents into images instead of initContainers if your policy restricts them
* Check logs on both application and the OTLP backend/Collector to confirm export success
### Disable exporting (collect-only / no-export mode)
Sometimes you may want to enable instrumentation but temporarily disable exporting (e.g., for smoke tests in Autopilot). You can turn off exporters while keeping instrumentation active.
* Cross-language (environment variables):
```bash
export OTEL_TRACES_EXPORTER=none
export OTEL_METRICS_EXPORTER=none
export OTEL_LOGS_EXPORTER=none
```
This disables all exporters. The SDK will still create spans/metrics/logs according to instrumentation, but they will not be sent to any backend.
* Java (OpenTelemetry Java Agent):
```bash
JAVA_TOOL_OPTIONS="-javaagent:/otel/opentelemetry-javaagent.jar \
-Dotel.traces.exporter=none \
-Dotel.metrics.exporter=none \
-Dotel.logs.exporter=none \
-Dotel.resource.attributes=service.name=sp-storage \
-Dotel.instrumentation.http.server.capture-request-headers=tracestate \
-Dotel.instrumentation.http.server.capture-response-headers=tracestate"
```
Or add these system properties directly to your JVM start command:
```bash
java -javaagent:/otel/opentelemetry-javaagent.jar \
-Dotel.traces.exporter=none \
-Dotel.metrics.exporter=none \
-Dotel.logs.exporter=none \
-Dotel.resource.attributes=service.name=sp-storage \
-Dotel.instrumentation.http.server.capture-request-headers=tracestate \
-Dotel.instrumentation.http.server.capture-response-headers=tracestate \
-jar app.jar
```
Note:
* Disabling exporters reduces external traffic and is useful for validation; however, instrumentation overhead still exists because spans/metrics/logs are created. For production, restore the desired exporters (e.g., set OTEL\_TRACES\_EXPORTER=otlp).
* Ensure resource attributes (service.name, namespace, version) remain configured so you can easily switch exporting back on later without needing to change application manifests.
---
---
url: /en/platform/production/dashboard-user-guide.md
---
# Softprobe Dashboard User Guide
Welcome to Softprobe Dashboard. This guide helps you quickly understand the interface, manage tenants and members, and operate key features consistently.
## Overview
## 🚀 Quick Start
### 1. Registration & Login
* Visit the homepage and click “Sign Up” (top right)
* Enter email and password, complete email verification
* Log in to start using the system
::: tip
Account setup and public key management are covered in the [Account Setup Guide](/en/platform/getting-started/account-setup).
:::
### 2. Interface Overview
After logging in, you will see:
* **Left Navigation**: Quick access to modules
* **Top Bar**: User info and tenant switching
* **Main Area**: Operational interface for current module
## 📊 Core Features
### 1. Dashboard Homepage
Real-time monitoring of core system metrics
#### Data Overview Cards
* **Database Usage**: Current storage usage (GB), usage percentage, color indicators
* **Data Entry Statistics**: Total records, filter by time range, historical comparison
* **Session Statistics**: Active sessions, total sessions in selected period, trend charts
* **Performance Metrics**: Average response time, P95 metrics, system health indicators
::: tip
For end-to-end user journey correlation, install the [SESSIFY](/en/platform/sessify) on your frontend and propagate sessionId through your service mesh.
:::
#### Operations
* Data loads once on page entry (no auto-refresh)
* To update: switch time range, switch tenant, or refresh page
### 2. Tenant Management
Resource isolation and management in multi-tenant environments
#### Tenant Switching
* Use the tenant selector in the top bar (search and filter supported)
* Click target tenant to switch
* Create new tenant via “+” button and fill basic info
#### Tenant Settings
* Basic info: name, description, icon
* Member management: add/remove, roles and permissions
* Public keys: create/manage keys and validity
* Danger zone: delete tenant, export backup, clear data
### 3. Team Member Management
Permission control and collaborative management
#### Adding Members
* Go to Tenant Settings → Member Management
* Click “Add Member”, enter email, choose role
* Roles: **Administrator**, **Editor**, **Viewer**
#### Role Permissions
* **Administrator**: Full control, manage members/settings, Public keys, dangerous ops
* **Editor**: Operate data and monitoring configs, cannot manage members/settings
* **Viewer**: Read-only access, suitable for report viewers
::: tip Quick reminder
If you need higher quotas or custom retention, contact support@softprobe.ai with your tenant id and expected workload.
:::
See the [FAQ](/en/platform/support/faq) for common questions, including data storage and security, mobile access, and export options.
---
---
url: /en/platform/configuration/config.md
---
# Configuration Guide
Learn how to configure SP-Istio Agent for your specific use cases.
::: info Before You Begin
* Complete [Account Setup](/en/platform/getting-started/account-setup) to get your public key
* Have your `minimal.yaml` configuration file ready
* Basic understanding of YAML and regular expressions
:::
## Overview
This guide covers:
* Understanding the configuration structure
* Setting up collection rules
* Configuring service discovery
* Advanced configuration options
## Configuration Structure
The SP-Istio Agent configuration is defined in a `minimal.yaml` file that contains three main components:
### 1. WasmPlugin Resource
The core component that loads the SP-Istio Agent into Istio's Envoy proxies:
```yaml
apiVersion: extensions.istio.io/v1alpha1
kind: WasmPlugin
metadata:
name: sp-istio-agent
namespace: istio-system
spec:
url: oci://docker.io/softprobe/softprobe:latest
pluginConfig:
# Your configuration goes here
```
### 2. EnvoyFilter Resource
Configures Envoy proxy settings for optimal performance:
```yaml
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: sp-istio-agent-config
namespace: istio-system
spec:
configPatches:
# Envoy-specific configurations
```
### 3. Network Resources
Defines network policies and service discovery settings.
## Core Configuration Options
### Basic Settings
| Field | Type | Description | Default |
| ---------------- | ------ | ------------------------------- | ------------------------ |
| `public_key` | string | Your Softprobe public key | Required |
| `sp_backend_url` | string | Softprobe collection endpoint | `https://o.softprobe.ai` |
| `service_name` | string | Override service name detection | Auto-detected |
### Example Basic Configuration
```yaml
pluginConfig:
public_key: "your-public-key-here"
sp_backend_url: "https://o.softprobe.ai"
traffic_direction: "server"
collectionRules:
http:
server:
- path: ".*"
```
::: tip
Always start with the basic configuration and gradually add collection rules based on your specific needs.
:::
## 🎯 Collection Rules
Collection rules define what traffic to capture and analyze. They support both `SERVER` (inbound) and `CLIENT` (outbound) modes through the `traffic_direction` setting.
### Rule Structure
**For SERVER mode (inbound traffic):**
```yaml
pluginConfig:
traffic_direction: "server"
collectionRules:
http:
server:
- path: ".*" # Regex pattern for URL paths
```
**For CLIENT mode (outbound traffic):**
```yaml
pluginConfig:
traffic_direction: "client"
collectionRules:
http:
client:
- host: ".*" # Regex pattern for hostnames
paths: [".*"] # List of regex patterns for paths
```
### SERVER Mode Rules
Capture inbound requests to your services:
```yaml
pluginConfig:
traffic_direction: "server"
collectionRules:
http:
server:
# Capture all API endpoints
- path: "/api/.*"
# Capture specific endpoints
- path: "/health"
- path: "/metrics"
- path: "/api/v1/users"
```
### CLIENT Mode Rules
Capture outbound requests from your services:
```yaml
pluginConfig:
traffic_direction: "client"
collectionRules:
http:
client:
# Capture all outbound requests to any host
- host: ".*"
paths: [".*"]
# Capture specific API calls to external services
- host: ".*\\.googleapis\\.com"
paths: ["/maps/api/.*", "/drive/v3/files"]
# Capture requests to specific service
- host: "my-api\\.com"
paths: ["/api/.*"]
```
***
## Collection Rules Explained (`collectionRules`)
`collectionRules` is the most central and flexible configuration item in the SP-Istio Agent. It allows you to precisely define which HTTP traffic to collect, thus avoiding unnecessary data reporting, saving costs, and improving efficiency.
### Using Regular Expressions
In all collection rules, the values of the `path`, `host`, and `paths` fields are **regular expressions (Regex)**. This provides you with powerful matching capabilities.
* To match everything, use `.*`.
* For an exact match, write the string directly, for example, `"/api/users"`.
* To match a specific pattern, use regex syntax, for example, `"/api/v[0-9]+/items"` can match `/api/v1/items` and `/api/v2/items`.
**Note:** If an invalid regular expression is provided, the system automatically falls back to **exact string matching**.
## Service Discovery: Automatically Identifying Your Services
In a complex microservices environment, accurately identifying each service is crucial. The SP-Istio Agent achieves this through an `EnvoyFilter` named `inject-app-name-header`.
This `EnvoyFilter` executes a small piece of Lua script before processing each request. The sole purpose of this script is to **find the name of the current service** and add it to a special HTTP header, `x-sp-service-name`.
**How does it work?**
The script sequentially attempts to get the service name from several common Kubernetes environment variables, such as `OTEL_SERVICE_NAME`, `APP_NAME`, `SERVICE_NAME`, or by parsing it from `POD_NAME`. This multi-source approach greatly improves the accuracy of automatic service identification, and **you usually do not need any additional configuration**.
***
## External Communication: Connecting to the Softprobe Backend
To enable the SP-Istio Agent to send data to `o.softprobe.ai`, we need to explicitly authorize this external communication in Istio. This is done through two resources: `ServiceEntry` and `DestinationRule`.
* **`ServiceEntry`**: Adds `o.softprobe.ai` to Istio's service registry, making it a legitimate external service.
* **`DestinationRule`**: Configures TLS encryption for traffic to `o.softprobe.ai`, ensuring secure data transmission.
In short, these two resources together open a secure channel for the SP-Istio Agent to the Softprobe backend.
We hope this detailed guide helps you better understand and use the SP-Istio Agent. If you have any questions, please feel free to contact us!
## Who should use this guide
This guide is intended for:
* Platform/SRE engineers managing Kubernetes + Istio deployments
* Dev teams needing fine-grained collection rules
* Anyone customizing Softprobe to match business flows
## Next Steps
* Deploy or update your cluster with the [Installation Guide](/en/platform/deployment/installation)
* Verify data in the [Softprobe Dashboard](https://dashboard.softprobe.ai)
* Add client-side visibility via the [SESSIFY](/en/platform/sessify)
***
## Related Topics
* [Quick Start Guide](/en/platform/getting-started/quick-start)
* [Core Concepts](/en/platform/advanced-guides/concepts)
* [FAQ / Troubleshooting](/en/platform/support/faq)
---
---
url: /en/platform/advanced-guides/concepts.md
---
# Understanding Softprobe Concepts
Learn how Softprobe works, its architecture, and core principles for effective observability.
## 📋 Overview
Softprobe is a comprehensive observability platform designed for modern cloud-native applications. It provides:
* **Full-stack tracing** across services and user interactions
* **Performance monitoring** with rich context and correlations
* **Authentication-centered architecture** with public key authentication
* **Kubernetes-native integration** for seamless deployment
## 🏗️ How Softprobe Works
### Architecture Overview
Softprobe operates through a distributed architecture:
### Key Components
#### 1. SP-Istio Agent (Service Mesh Layer)
* **WASM Plugin**: Runs within Istio sidecars to capture service-to-service traffic
* **Envoy Filter**: Injects service identity headers and manages traffic capture
* **Zero Code Changes**: Works transparently with your existing applications
#### 2. SESSIFY (Frontend Layer)
* **Automatic Instrumentation**: Captures user interactions, performance metrics, and errors
* **Session Management**: Generates a per-tab sessionId and reuses it across navigations within the same tab; all frontend telemetry carries the sessionId for end-to-end correlation with backend traces and logs
* **Lightweight**: Minimal performance impact on your web applications
#### 3. Softprobe Backend
* **Collectors**: Receive and process observability data from all sources
* **Processing Engine**: Correlates, enriches, and stores telemetry data
* **Dashboard**: Visualizes insights and provides analytics capabilities
## 🔄 Data Flow
### Request Journey Through Your System
1. **User Interaction**: User interacts with your web application
2. **Frontend Capture**: SESSIFY captures performance metrics and user actions
3. **Backend Request**: Application makes service-to-service calls
4. **Mesh Capture**: SP-Istio Agent captures traffic within the service mesh
5. **Data Enrichment**: Service identity and context are added
6. **Transmission & Authentication**: Data is sent to the Softprobe backend and authenticated using public key signatures
7. **Correlation & Analysis**: Backend correlates frontend and backend data
8. **Visualization**: Insights are displayed in the dashboard
### Example: User Login Flow
```
1. User clicks "Login" button
→ SESSIFY captures click event
2. Frontend sends login request to backend
→ SESSIFY traces the API call
3. Backend service processes login
→ SP-Istio Agent captures service traffic
4. Backend calls user database
→ SP-Istio Agent captures database call
5. All data correlated by session ID
→ Softprobe shows complete user journey
```
## 🔐 Authentication & Data Transmission
### Public Key Authentication
Softprobe uses asymmetric cryptography for authentication:
* **No Secrets in Configuration**: Your public key is not sensitive
* **Cryptographic Proofs**: Requests are signed and verified
* **Easy Rotation**: Public keys can be changed without downtime
* **Audit Trail**: Authentication events are logged
### Data Handling & Transport
* **TLS Encryption**: Data is encrypted in transit
* **No Sensitive Storage**: SP-Istio Agent doesn't store sensitive data
* **Least Privilege**: Minimal permissions required for operation
* **Policy-Compatible**: Designed to work within common policies
## 🎯 Core Concepts
### Distributed Tracing
Softprobe provides end-to-end tracing across:
* **Frontend Applications**: User interactions and browser performance
* **Backend Services**: Service-to-service communication
* **External Dependencies**: Database calls, external API requests
* **Infrastructure**: Kubernetes pod and node context
### Service Mesh Integration
Leveraging Istio's capabilities:
* **Automatic Sidecar Injection**: No code changes required
* **Traffic Capture**: Both inbound and outbound traffic
* **Service Identity**: Automatic service discovery and naming
* **Zero-Trust Policies**: Works within service mesh policies
### Observability Data Model
Softprobe captures and correlates:
* **Spans**: Individual operations within a trace
* **Traces**: Complete request journeys across services
* **Metrics**: Performance measurements and resource usage
* **Logs**: Application and infrastructure logs
* **Events**: User interactions and business events
## ⚡ Performance Considerations
### Efficient Data Collection
* **Sampling**: Configurable sampling rates to manage data volume
* **Body Capture Limits**: Control payload sizes to minimize overhead
* **Local Processing**: Lightweight processing within the agent
* **Batched Transmission**: Efficient network utilization
### Resource Optimization
* **Memory Efficient**: WASM plugins have minimal memory footprint
* **CPU Lightweight**: Optimized processing algorithms
* **Network Efficient**: Compression and batching of telemetry data
* **Scalable Architecture**: Handles high-volume production workloads
## 🚀 Getting Started Concepts
### Development vs Production
* **Development**: Quick testing with local clusters and sample data
* **Staging**: Validate configuration with production-like environments
* **Production**: Full-scale deployment with optimized settings
### Environment Strategy
* **Separate Public Keys**: Use different keys for each environment
* **Targeted Deployment**: Deploy to specific namespaces as needed
* **Gradual Rollout**: Start with monitoring, then add tracing and capture
## 📊 What to Expect
### Immediate Benefits
* **Service Dependency Mapping**: Automatic discovery of service relationships
* **Performance Baselines**: Establish normal performance patterns
* **Error Detection**: Identify failing requests and services
* **User Experience Insights**: Understand real user behavior
### Advanced Capabilities
* **Root Cause Analysis**: Drill down into performance issues
* **Capacity Planning**: Identify resource bottlenecks
* **Business Metrics**: Correlate technical data with business outcomes
* **Proactive Monitoring**: Alert on anomalies before users are affected
## 🔧 Integration Patterns
### Microservices Architecture
Ideal for distributed systems with:
* Multiple interacting services
* Complex dependency graphs
* Cross-service troubleshooting needs
* Performance optimization requirements
### Monolithic Applications
Also valuable for single applications with:
* Internal component tracing
* External dependency monitoring
* User experience correlation
* Performance optimization opportunities
### Hybrid Environments
Works across diverse infrastructure:
* Kubernetes clusters
* Virtual machines
* Cloud services
* On-premises infrastructure
## 🎯 Next Steps
Now that you understand Softprobe's concepts:
1. **Set Up Your Account**: [Account Setup & Public Key Management](/en/platform/getting-started/account-setup)
2. **Try Quick Start**: [Quick Start Guide](/en/platform/getting-started/quick-start) for local testing
3. **Deploy to Production**: [Production Installation Guide](/en/platform/deployment/installation)
4. **Configure Advanced**: [Configuration Guide](/en/platform/configuration/config) for custom rules
## ❓ Frequently Asked Questions
### Q: Does Softprobe affect application performance?
**A**: The impact is minimal. SP-Istio Agent uses efficient WASM plugins and the SESSIFY is lightweight. Sampling can be configured to balance insight and overhead.
### Q: How is my data transmitted and authenticated?
**A**: All data is encrypted in transit (TLS) and authenticated via public key signatures. No sensitive credentials are stored in your configuration.
### Q: Can I use Softprobe with existing monitoring tools?
**A**: Yes. Softprobe complements existing tools by providing deeper distributed tracing and user experience correlation.
### Q: How quickly can I see results after installation?
**A**: Immediately. Data starts flowing to the dashboard as soon as the agent is deployed and your applications receive traffic.
***
## 📚 Additional Resources
* [Istio Documentation](https://istio.io/latest/docs/)
* [OpenTelemetry Concepts](https://opentelemetry.io/docs/concepts/)
* [Service Mesh Best Practices](https://istio.io/latest/docs/ops/best-practices/)
* [Observability Fundamentals](https://opentelemetry.io/docs/concepts/observability-primer/)
## 🆘 Support
Need help understanding concepts?
* **Documentation**: Browse our comprehensive guides
* **Community**: Join discussions with other users
* **Support**: Contact our team for technical assistance
***
*Last updated: January 2024*
*Version: v1.0*
---
---
url: /en/platform/advanced-guides/agent-architecture.md
---
# SP-Istio Agent Architecture
This page explains the server-side collection component of Softprobe — the SP‑Istio Agent — including how it is injected, what data it captures, and how it transmits telemetry securely and efficiently.
## Overview
* Deployment: Injected into Istio's Envoy sidecar via WasmPlugin
* Implementation: Rust + WebAssembly (Wasm)
* Capture scope: HTTP request/response, context headers, status codes, latency, and custom tags
* Transmission: Asynchronous batched delivery to the Softprobe backend with retry and queueing
## How It Works
1. Request enters Envoy → the Wasm filter intercepts and parses key fields (method, path, status code, headers, etc.)
2. Collection rules (collectionRules) decide whether to capture the event
3. The event is built and appended to an internal buffer/queue
4. Batches are sent asynchronously to the backend (e.g., o.softprobe.ai), carrying your `public_key` for authentication
5. The backend stores and indexes data; the Dashboard provides session views and cross‑service call trees
## Configuration Highlights
* `traffic_direction`: choose `server` (inbound) or `client` (outbound)
* `collectionRules`: use regex for `host`/`path` to precisely control capture scope
* Service discovery: an EnvoyFilter (Lua) injects `x-sp-service-name` automatically
* External communication: use ServiceEntry + DestinationRule to authorize secure egress to the Softprobe backend
## Performance & Reliability
* Low overhead: Wasm runs inside Envoy with no language/framework intrusion
* Asynchronous delivery: minimizes impact on business requests; supports backpressure and retry
* Resource control: rate limiting and queue sizing to avoid peak pressure
## Security
* Transport encryption: TLS, with egress restricted to approved backend domains
* Authentication: a public key validates tenant identity
* Privacy & compliance: field masking/redaction and configurable capture scope
## Working with OTLP
* Use side‑by‑side: the Agent provides zero‑intrusion HTTP capture; OTLP receives data from SDKs/Collectors
* The backend unifies indexing and correlation, enabling session and trace views across data sources
## Next Steps
* Read the configuration guide and deploy the Agent in a test environment
* Validate data and observability in the Dashboard
* Expand collection rules and tagging progressively based on business scenarios
---
---
url: /en/platform/billing/pricing.md
---
# Plans & Pricing
This page helps you understand Softprobe plan types, quotas, and billing models. Naming is unified: “Pro” in the Dashboard corresponds to “Business” in backend systems — they are the same plan.
## Plan Overview
Softprobe offers three plan families designed for different stages and scale:
* Free — fixed quota, best for evaluation
* Business — fixed quota, suitable for growing teams
* Enterprise — usage‑based billing, scales with demand; for Enterprise support, please contact support@softprobe.ai
## Quotas & Limits
Quotas vary by plan and are designed to balance performance, cost, and reliability.
* Free
* Storage quota appropriate for evaluation
* Limited seats and API rate limits
* Project and Public key limits
* Over‑quota behavior: blocked
* Business (fixed quota)
* Larger storage quota for mid‑sized teams
* More seats and higher API rate limits
* More projects and Public keys
* Over‑quota behavior: guided upgrade or additional charges (depending on account settings)
* Enterprise (usage‑based)
* No fixed quotas — you pay for what you use
* Common usage dimensions include storage, API calls, and monthly active users
For your exact quotas and entitlements, refer to the Billing section of your Dashboard.
## Billing Models
* Free: $0 per month
* Business (fixed quota): fixed monthly fee
* Enterprise (usage‑based): billed by actual usage (e.g., storage, API calls, active users)
Billing cycles are typically monthly. Charges and invoices are generated based on your plan and actual usage.
## Upgrades & Consistent Naming
* Free → Business (fixed quota): supported; can take effect immediately or at the next billing cycle
* Business (fixed quota) → Enterprise (usage‑based): supported; ideal for fast‑growing or variable workloads
If you need higher quotas or custom terms, contact support@softprobe.ai.
---
---
url: /en/platform/billing/subscriptions.md
---
# Billing & Subscriptions
This page explains the subscription lifecycle, billing cycles and usage, cancellation and resumption, and how plan changes affect your account.
## Subscription Lifecycle
Common states:
* ACTIVE — the subscription is active; billed by cycle or usage
* SUSPENDED — the subscription is paused; service is restricted or stopped
* CANCELLED — the subscription is cancelled; service stops after the current period (or immediately, depending on settings)
Typical transitions:
* Payment failure or insufficient balance triggers SUSPENDED
* User‑initiated cancellation, or “cancel at period end”
* Risk controls may degrade or stop service
## Billing Cycle & Usage
* Fixed plans: a fixed monthly fee; not affected by usage within the cycle
* Usage‑based: usage is aggregated monthly and billed at the end of the cycle
* Usage records and billing history are available for reconciliation and reporting
## Cancellation & Resumption
* Cancellation: choose “cancel immediately” or “cancel at the end of the period”
* Resumption: pay outstanding balances or complete payment to restore ACTIVE state
* Data retention: data is typically retained during suspension or cancellation; may be cleaned up after grace periods (see Terms)
## Plan Changes (Upgrade / Downgrade / Switch Billing Models)
* Free → Pro (Business): upgrade supported; can take effect immediately or at the next billing cycle
* Pro (Business) → Pay‑as‑you‑go (Enterprise): switch supported; you may configure usage caps and alerts
* Downgrade policy: if you downgrade from a higher‑quota plan, over‑quota data may be restricted or require migration
* Proration: mid‑cycle plan changes may trigger proration
## Risk Controls & Spending Limits
* Balance threshold alerts (e.g., reminder at $10, urgent at $5)
* Service degradation strategies: RESTRICTED (strict limits), SUSPENDED (stop service)
* Daily/monthly spending caps and automatic stop‑service protections to prevent unexpected costs
## Stripe Integration & Price Mapping
* Each plan maps to Stripe products and prices (fixed and metered)
* Subscriptions are billed based on these mappings
* Price changes take effect in the next billing period or are prorated based on settings
## Common Actions
* View current subscription and billing cycle: Dashboard → Subscription panel
* Change plan: Dashboard → Subscription panel (Pro/Business, Pay‑as‑you‑go)
* View usage and cost estimates: Dashboard → Usage & Cost
* Cancel or pause: Dashboard → Subscription panel (supports cancel at period end)
## Support
* Enterprise customization: support@softprobe.ai
* Technical questions: see API documentation and FAQ (Support section)
---
---
url: /en/platform/sessify.md
---
# SESSIFY Integration
**Lightweight • Zero-dependency • W3C-compliant distributed tracing**
SESSIFY is Softprobe's session lifecycle management and distributed tracing SDK, specifically designed for frontend application session management and request tracing. It automatically handles session creation, validation, keep-alive, and expiration logic, while injecting W3C-standard trace context into HTTP requests to achieve end-to-end session correlation.
Key benefits include automated session management, distributed tracing across microservices, security with Web Crypto API, and zero dependencies for minimal bundle size.
::: info
The Quick Start environment already has the SESSIFY (`@softprobe/sessify`) pre-installed and enabled. This document is for integrating the SDK into your own frontend applications (React/Vue/Next.js, etc.).
:::
## Prerequisites
* A modern web application (React, Vue, Next.js, or plain JavaScript)
* Node.js 16+ and a bundler (e.g., Vite, Webpack)
## Installation
```bash
# npm
npm install @softprobe/sessify
# yarn
yarn add @softprobe/sessify
# pnpm
pnpm add @softprobe/sessify
```
## Initialization
```ts
// React / Next.js Example
import { useEffect } from 'react';
import { initSessify } from '@softprobe/sessify';
useEffect(() => {
// Initialize with default settings
initSessify({
});
}, []);
// Vanilla JS Example
import { initSessify } from '@softprobe/sessify';
initSessify({
});
```
## Key Features
## Core Usage
Once initialized, you can manage sessions anywhere in your application.
```javascript
import { getSessionId, startSession, endSession, isSessionActive } from '@softprobe/sessify';
// 1. Get Current Session
// If expired or missing, this automatically creates a new one.
const sessionId = getSessionId();
console.log('Active Session:', sessionId);
// 2. Check Status
if (isSessionActive()) {
console.log('User is currently active');
}
// 3. Force Refresh (e.g., on Login)
// Invalidates the old session and starts a fresh one immediately.
const newSessionId = startSession();
// 4. Logout / Cleanup
// Clears storage and invalidates the session.
endSession();
```
## Configuration
The initSessify function accepts a configuration object to tailor behavior to your needs.
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| customTraceState | object | {} | Custom key-value pairs to include in tracestate headers. If provided |
| sessionStorageType | session/local | 'session' | Controls persistence. 'local' survives browser restarts; 'session' clears when the tab closes. |
### Custom Trace State Example
Enhance your request tracing by adding context like environment or version:
```javascript
initSessify({
sessionStorageType: 'local',
customTraceState: {
'x-sp-env': 'production',
'x-sp-ver': '1.0.0',
'x-sp-tier': 'premium'
}
});
```
Resulting Header: tracestate: x-sp-session-id=...,x-sp-env=production,x-sp-ver=1.0.0...
## Technical Details
### Session Lifecycle Strategy
* **Creation**: Uses a base36 timestamp combined with a Web Crypto API random string for a ~16 character unique ID.
* **Validation**: Automatically checks for timeout on every access. If the timeout is exceeded, the old session is discarded and a new one is seamlessly generated.
### HTTP Interception
@softprobe/sessify monkey-patches the global fetch API (safely) to:
* Check if a session is active.
* Inject the tracestate header conforming to W3C standards.
* Append your siteName or customTraceState and the current session\_id.
---
---
url: /en/platform/support/faq.md
---
## FAQ
### Q1: What if I lose the minimal.yaml configuration file?
**A:** The `minimal.yaml` file is only downloaded once when you create a public key. If you lose it:
1. Delete the current public key from your dashboard
2. Create a new public key (this will generate a new `minimal.yaml` file)
3. Save the new configuration file immediately
### Q2: Is my public key a secret?
**A:** No, your public key is not a secret. It's designed to be publicly visible and can be safely stored in version control. Integrity and authenticity are guaranteed by asymmetric cryptography: telemetry is signed (or authenticated) using mechanisms tied to your tenant and verified server-side against known public keys. No sensitive private keys are stored in your Kubernetes configuration.
### Q3: Can a public key be reused across environments?
**A:** Yes, a public key can be used in multiple environments, but we recommend:
* Use separate public keys for production and non-production environments
* This allows for better access control and auditing
* Different environments may have different rate limits and permissions
### Q4: How do I manage multiple projects?
**A:** We recommend creating different tenant groups for different projects:
1. Create a separate tenant group for each project
2. Generate separate public keys for each project
3. Invite relevant team members to the corresponding tenant groups
### Q5: Are there usage limits for public keys?
**A:** Usage limits may include:
* Request rate limits based on your subscription plan
* Data storage quotas
* Feature permission restrictions
* Check your subscription plan for specific limits
### Q6: What if there is no data after configuration?
**A:** Please check:
1. If the `minimal.yaml` file was applied correctly with `kubectl apply -f minimal.yaml`
2. If the Istio service mesh is properly installed in your cluster
3. If the network connection is normal
4. If the application pods have been restarted after applying the configuration
5. If there are any error messages in the application logs
***
## Related Topics
* [Quick Start](/en/platform/getting-started/quick-start)
* [Installation Guide](/en/platform/deployment/installation)
* [Configuration Reference](/en/platform/configuration/config)
* [Core Concepts](/en/platform/advanced-guides/concepts)
## Key Features at a Glance
* Dashboard: Modern responsive UI, real-time analytics, heatmaps, and user session recordings; built with React + Vite + TailwindCSS.
* OTEL Backend: Ingests traces/logs/metrics; BigQuery storage with indexed request\_body\_hash for fast lookups and correlation.
* Auth Service: Multi-tenant authentication/authorization; Public key lifecycle, JWT utilities, role-based access (OWNER/ADMIN/MEMBER).
::: info Why this matters
These components work together to deliver end-to-end observability with session correlation, efficient data lookup, and secure multi-tenant controls.
:::
## Precautions & Best Practices
* Environment separation: Use distinct tenants/public keys for prod vs. non-prod; start with conservative sampling and ramp up.
* Public key hygiene: Full keys are shown only at creation; store securely; rotate periodically; never commit secrets.
* Rate limits: Each Public key has hourly limits; design clients with retries/backoff and monitor usage counters.
* Data sensitivity: Avoid sending PII in attributes; rely on signing/verification and TLS; prefer hashed request bodies for correlation.
* Performance: BigQuery indexes grow with data; combine filters with request\_body\_hash; consider time partitions and cache if needed.
* Deployment: Use CI/CD (e.g., ArgoCD) with image tags; ensure dashboard release and backend schema changes are coordinated.
::: tip Best practice
Create separate tenant groups and public keys per project/environment to ease auditing and access control.
:::
::: warning Caution
Do not paste full Public keys into issue trackers or chat tools. Use masked previews in UI and rotate compromised keys immediately.
:::
## Plans & Quotas
* Storage quota per tenant: Configurable (e.g., storageQuotaGb in tenant settings). Usage should be monitored and alerted.
* API rate limits: Per-key hourly caps (rateLimitPerHour). Plan capacity around expected ingestion/query workloads.
* Multi-tenant boundaries: Resources (datasets/buckets) are scoped per tenant and environment for isolation.
* Enterprise options: Higher quotas, SLOs, and custom retention are available. Contact support@softprobe.ai for details.
---
---
url: /en/testing.md
---
# Softprobe Testing
**Record real traffic. Replay with automatic mocks. Compare without writing test cases.**
::: tip Ready to start?
🚀 If you are new to Softprobe, go straight to our **[Getting Started](/en/testing/getting-started)** guide! Try it out using a pre-built JAR in 5 minutes, and then onboard your own application.
:::
Softprobe Testing is record-and-replay regression for **Java** services. Attach the Softprobe Java agent as a `-javaagent`, capture inbound API traffic and outbound dependency calls in the background, then replay recorded cases in a test environment while dependencies are mocked from stored data and results are compared automatically.
::: info Product areas on this site
| Area | You use it when... |
|------|---------------------|
| **[Platform](/en/platform/)** | You need Istio/Envoy mesh capture, SESSIFY session context, or the observability dashboard |
| **Testing** (this section) | You need Java record/replay, the JVM agent, policies, replay semantics, installation, commands, and automation |
:::
## Why record-and-replay
Traditional integration tests require maintaining environments, seed data, and hand-written cases. Softprobe Testing instead:
* **No application code changes** — bytecode instrumentation via `sp-agent.jar`
* **High coverage from real traffic** — production or staging requests become replay cases
* **Isolated replay** — databases, HTTP clients, Redis, RPC, caches, and more are mocked from recordings so you do not need live downstreams in the test environment
* **Safe WRITE paths** — recorded dependency behavior is replayed without dirtying shared databases during regression
* **Lower noise** — compare rules, time mocking, and ignore nodes handle timestamps, random IDs, and environment-specific fields
## How the pieces fit together
| Component | Role |
|-----------|------|
| **Your Java service** | The application under test, started with `-javaagent:…/sp-agent.jar` |
| **Softprobe Java agent** | Records and replays at runtime; mocks dependencies during replay |
| **Softprobe backend** (`:8090`) | Deployed via Helm. Stores cases (MongoDB), serves policies, runs replay plans, computes diffs |
| **`sp` command** (optional) | Registers apps, applies policies, starts replay, inspects failures — see [Commands](/en/testing/commands/) |
| **Dashboard / workbench** (optional) | Visual diff and trace review |
```mermaid
flowchart LR
App[JVM app under test]
Agent[Java agent]
Backend[sp-backend]
App --> Agent
Agent --> Backend
```
## Record → replay → compare (short)
1. **Record** — Agent captures entry traffic (e.g. HTTP `Servlet`) and dependency calls (`HttpClient`, `Database`, `Redis`, …) while the app handles real requests.
2. **Store** — Backend persists each interaction; replay hot path uses Redis-backed mock cache.
3. **Replay** — Schedule service sends recorded entry requests to your **test instance** (`targetEnv` URL). The agent returns recorded dependency responses instead of calling real downstreams.
4. **Compare** — Engine diffs recorded vs replay traffic; policies define what to ignore or how strictly to match.
Details: [Record traffic](/en/testing/recording) · [How it works](/en/testing/how-it-works) · [Replay and diff](/en/testing/replay-and-diff)
## Platform agent ≠ Java agent
The [Platform](/en/platform/advanced-guides/agent-architecture) **SP-Istio agent** runs in Envoy sidecars and captures mesh HTTP traffic. **Softprobe Testing** uses the **JVM agent** attached with `-javaagent`. They solve different problems; both can feed the same backend for correlation in SaaS deployments.
## Who should read this section
* **Java developers** adopting record/replay for the first time
* **QA / release engineers** running regression without full downstream stacks
* **Platform engineers** wiring `sp-backend` and agent startup in K8s or VM images
* **Automation authors** — start here for concepts, then [Commands](/en/testing/commands/) for `sp` automation
## Quick links
* [Getting Started](/en/testing/getting-started) — Try the 5-minute [Travel OTA](https://github.com/softprobe/demo-ota) demo and onboard your application.
* [Install Softprobe](/en/testing/installation/) — Install, set up, launch coding, diagnose, and upgrade with `sp`.
* [Record traffic](/en/testing/recording) — Core workflow step 1: build the case corpus.
* [Java agent](/en/testing/java-agent) — Attach, JVM flags, and production safety.
* [Policies overview](/en/testing/policies) — YAML by lifecycle phase.
* [Replay and diff](/en/testing/replay-and-diff) — Core workflow step 2: regression run.
* [Webhook and CI/CD](/en/testing/webhook-and-ci) — Post-deploy replay and pipeline gates.
* [Supported frameworks](/en/testing/supported-frameworks)
---
---
url: /en/testing/getting-started.md
---
# Getting Started with Softprobe Testing
Welcome to Softprobe Testing! This guide provides a single, cohesive path to get you started with record-and-replay in minutes using our pre-built Travel OTA demo application, followed by a quick wrapup on how to connect your own Java service.
::: tip Pick your interface
The steps below for registering an app, viewing recordings, and replaying can all be done in the **Web console** or with the **`sp` command**. Use the switch at the top of each card to choose one — your choice is remembered and stays consistent across the whole docs site.
:::
***
## Prerequisites
* **Java 8 or higher** (Java 17/21 recommended) installed and on your `PATH`.
* **`sp` command** installed (`curl -fsSL https://install.softprobe.ai/install.sh | bash`) — see [Install the CLI](/en/testing/installation/).
* Your **Softprobe Helm chart** is deployed and running (with the Softprobe backend available, e.g. at `http://:8090`) — not deployed yet? Follow [Server (Helm)](/en/testing/installation/server) first.
***
## 1. Verify Java Installation
Ensure Java is configured correctly and available in your terminal:
```bash
java -version
```
## 2. Download the Demo Application
Download the pre-built [Travel OTA](https://github.com/softprobe/demo-ota) application JAR (either [click to download travel-ota.jar](https://github.com/softprobe/demo-ota/releases/download/v1.1.0/travel-ota.jar) directly via your browser, or run the command below):
```bash
curl -L -O https://github.com/softprobe/demo-ota/releases/download/v1.1.0/travel-ota.jar
```
## 3. Download the Softprobe Agent
Download the Softprobe Java agent JAR (either [click to download sp-agent.jar](https://install.softprobe.ai/artifacts/agent/latest/sp-agent.jar) directly via your browser, or run the command below):
```bash
curl -fsSL -o sp-agent.jar https://install.softprobe.ai/artifacts/agent/latest/sp-agent.jar
```
See [Download Java agent](/en/testing/download-java-agent) for immutable release versions.
## 4. Register the Application
Register the demo application in Softprobe to receive a unique `appId` (a 16-character hex identifier):
1. Open your Softprobe Dashboard.
2. Navigate to **Apps** and click **Create App**.
3. Enter `travel-ota` as the name and click **Save**.
4. Copy the generated **App ID**.
Run the following command to register the app with `sp`:
```bash
export SP_API_URL=http://localhost:8090 # Point to your Helm/local backend
sp app create travel-ota
```
Save the `appId` returned in the JSON response.
## 5. Start the Application with the Agent
Start the demo app with the `-javaagent` flag, passing your `appId`:
```bash
java -javaagent:sp-agent.jar \
-Dsp.app.id= \
-Dsp.api.url=http://localhost:8090 \
-jar travel-ota.jar
```
*(If your Helm backend is hosted at another address, replace `http://localhost:8090` with your actual backend URL).*
The application is now running locally at .
## 6. Perform a Booking (Generate Traffic)
Open in your browser:
1. Click **Search** to view available flights.
2. Select a flight and click **Book**.
3. Complete the checkout/payment process.
The Softprobe agent automatically intercepts and captures this entire transaction.
## 7. View Recorded Data
1. Log in to your Softprobe Dashboard.
2. Go to the **Workbench** or **Recordings** tab.
3. Select `travel-ota` from the app dropdown.
4. Browse the recorded traces and inspect the deep dependency graphs.
List cases recorded for the app in the last 10 minutes:
```bash
sp record case list --app --since -10m
```
## 8. Replay the Recordings
Replay executes recorded transactions against a target environment with automated dependency mocking (your database and downstreams do not need to be set up).
1. Navigate to the **Replays** tab and click **New Replay Plan**.
2. Select `travel-ota`.
3. Choose the cases to replay.
4. Set the target environment to `http://localhost:8080` and click **Run**.
Trigger the replay plan pointing to your local running instance:
```bash
sp replay run --app --env http://localhost:8080
```
Watch the replay status until it reaches a terminal state:
```bash
sp replay status --watch
```
***
## Onboard Your Own Application
Onboarding your own service is **exactly the same** as running the Travel OTA demo! Your application is just another `.jar` file started with the same Softprobe Java Agent and your custom App ID.
To connect your own application:
1. **Register Your App**: Create a new 16-character App ID either via **Apps** → **Create App** in the Web UI, or run:
```bash
sp app create
```
2. **Attach the Agent**: Start your own JVM service with the same `-javaagent` flag, passing your new App ID and Helm backend address:
```bash
java -javaagent:sp-agent.jar \
-Dsp.app.id= \
-Dsp.api.url=http://:8090 \
-jar your-own-application.jar
```
3. **Verify and Replay**: Record traffic, list cases, and trigger replays exactly as you did with the demo app.
## Next: the core workflow
The demo is just the start. Take your own application through the **core workflow**:
1. **[Record traffic](/en/testing/recording)** — capture real cases from production/staging and build your regression corpus
2. **[Replay & diff](/en/testing/replay-and-diff)** — run a regression before every release
3. **[Review diffs](/en/testing/review-diffs-in-the-web-ui)** — understand failing cases and accept the differences that aren't bugs
4. **[Configure compare rules](/en/testing/compare-rules-web-ui)** — turn always-changing fields into rules so they stop false-alarming
For deeper configuration, see [Java Agent Configuration](/en/testing/java-agent) (JVM properties, Tomcat/Docker) and the [Policies Overview](/en/testing/policies) (declarative YAML for CI/GitOps).
---
---
url: /en/testing/how-it-works.md
---
# How record-and-replay works
Softprobe Testing captures a **transaction**: one inbound request plus every dependency the code touched on that path. On replay, the same code path runs again; external calls are satisfied from stored data instead of live systems.
## End-to-end sequence
```mermaid
sequenceDiagram
participant App as App under test
participant Agent as Java agent
participant Storage as sp-backend storage
participant Redis as Redis mock cache
participant Mongo as MongoDB
participant Schedule as Replay schedule
Note over App,Mongo: Recording
App->>Agent: dependency call
Agent->>Storage: upload SpMocker
Storage->>Mongo: persist
Note over Schedule,Redis: Replay prep
Schedule->>Storage: preload mocks
Storage->>Redis: record category keys
Note over App,Redis: Replay
App->>Agent: same call path
Agent->>Storage: query mock
Storage->>Redis: get recorded response
Storage-->>Agent: Zstd payload
Agent-->>App: mocked dependency
Note over Schedule,Mongo: Compare
Schedule->>Storage: record vs replay lists
Storage->>Schedule: diff results
```
## Recording phase {#recording-phase}
When recording is enabled, the agent intercepts:
| Kind | Examples | What is stored |
|------|----------|----------------|
| **Entry** | `Servlet`, `DubboProvider`, `NettyProvider` | Inbound API request and response |
| **Dependency** | `HttpClient`, `Database`, `Redis`, `DubboConsumer`, `DynamicClass`, … | Outbound request and response per call |
Each interaction is a **mocker** row keyed by `appId`, trace/case identifiers, category, and operation name.
**How to produce cases:** [Record traffic](/en/testing/recording) · **Recording scope config:** [Recording policy](/en/testing/policies#recording-policy)
::: tip
Cases are created only from **instrumented traffic**. There is no supported workflow to hand-author cases in the CLI; send real or synthetic traffic through the app while the agent is recording.
:::
## Replay phase
A **replay plan** selects recorded cases and drives entry HTTP traffic to **`targetEnv`** — the base URL of the service under test (for example `http://order-service.test:8080`). This is **not** the same as `SP_API_URL`, which points at sp-backend.
During replay:
1. The schedule service sends the recorded entry request to `targetEnv`.
2. The app under test must run with the agent attached (recording frequency can be set to zero on the replay machine).
3. On each dependency call, the agent asks storage for the **recorded response** matching that call.
4. Storage serves from Redis when preloaded; otherwise it loads from MongoDB.
**Entry vs dependency behavior:**
* **Entry** (`entryPoint` categories): storage records the replay request; the live app still executes your controller/handler.
* **Dependency**: storage returns the recorded mock body so the app does not hit the real database or external API.
## Compare phase
After replay, the compare engine pairs recorded and replay mockers. Failures mean a mismatch on the main response or a dependency (missing call, wrong value, extra call).
Typical diff patterns:
* **Value diff** — same dependency was called but response body differs
* **Missing call** — replay did not invoke a dependency that was recorded
* **Extra call** — replay invoked something not present in the recording
Use [compare policy](/en/testing/policies) and [Replay and diff](/en/testing/replay-and-diff) to ignore noisy fields (timestamps, tokens, IPs).
## Pedagogical example
Consider a method that parses an IP string and calls a validator:
```java
public Integer parseIp(String ip) {
int result = 0;
if (checkFormat(ip)) {
String[] ipArray = ip.split("\\.");
for (int i = 0; i < ipArray.length; i++) {
result = result << 8;
result += Integer.parseInt(ipArray[i]);
}
}
return result;
}
```
**Recording** — the agent saves arguments and return values when `needRecord()` is true.
**Replay** — the agent short-circuits with stored results so `checkFormat` and parsing behave as they did during capture, even if the test environment differs.
Dynamic classes (local cache, encryption helpers, system time) use the same model; configure them via recording policy and mock policy rather than changing application code.
## Storage layout (conceptual)
| Store | Role |
|-------|------|
| **MongoDB** | Durable recordings, replay plans, compare results |
| **Redis** | Hot mock cache during replay (`record:{category}:{recordId}:…`) |
When self-hosted, `sp-backend` is typically a single process on port **8090** serving API, storage, and schedule.
## Related
* [Java agent](/en/testing/java-agent)
* [Policies](/en/testing/policies)
* [Replay and diff](/en/testing/replay-and-diff)
* [CLI concepts](/en/testing/agents/concepts)
---
---
url: /en/testing/installation/server.md
---
# Install Softprobe Server
Install the unified Softprobe backend on Kubernetes with Helm. The chart deploys **Redis** in-cluster and either **bundled MongoDB** or connects to your **existing MongoDB** server.
Chart **v4.3.x+** also enables the [unified log pipeline](#unified-log-pipeline) by default (Vector, Parquet PVC, compaction). Fresh installs need only the MongoDB and encryption keys below — no separate `logPipeline` block required.
**Prerequisites:** Kubernetes 1.24+, Helm 3.x, GCR pull credentials from Softprobe, and `encryption.secretKey` for at-rest payload encryption.
For **bundled** MongoDB, your cluster needs a default or configured `StorageClass` for the MongoDB PVC.
## MongoDB modes (pick one)
Configure **exactly one** mode in your values file. `helm install` fails if both are set or neither is set.
| Mode | Set in values | Chart deploys MongoDB? |
|------|---------------|------------------------|
| **Bundled** | `mongodb.bundled.auth.password` | Yes — Deployment, Service, PVC |
| **External** | `mongodb.connectionString` | No |
**Shared external MongoDB:** multiple Helm releases can use one MongoDB host. Put a **unique database name** in each connection string (for example `acme_prod_sp_storage_db`).
Download the example values file (always the current version):
[values.example.yaml](https://storage.googleapis.com/softprobe-published-files/helm/sp-backend/latest/values.example.yaml)
## Install
### 1. Helm repo and namespace
```bash
helm repo add softprobe \
https://storage.googleapis.com/softprobe-published-files/helm/sp-backend
helm repo update
kubectl create namespace softprobe
```
### 2. GCR pull secret
```bash
kubectl create secret docker-registry softprobe-gcr-pull \
--docker-server=https://gcr.io \
--docker-username=_json_key \
--docker-password="$(cat softprobe-registry-puller.json)" \
--namespace softprobe
```
::: warning
Use both `gcr.io` and `https://gcr.io` auth entries. See the chart README if plain `kubectl create secret docker-registry` causes `ImagePullBackOff`.
:::
### 3. Values file
Copy the example to `values.yaml` and edit for your environment. **Pick one MongoDB block below.**
#### Mode A — Bundled MongoDB (in-cluster)
Chart deploys MongoDB 7 and Redis 7 alongside sp-backend.
```yaml
image:
tag: "v4.3.10"
pullSecrets:
- name: softprobe-gcr-pull
mongodb:
bundled:
auth:
password: "CHANGE_ME_mongo_password"
# Optional: storageClass, storageSize, resources, placement — see values.example.yaml
encryption:
enabled: true
secretKey: "CHANGE_ME_base64_32_byte_key"
```
#### Mode B — External MongoDB
Use your existing MongoDB server. Do **not** set `mongodb.bundled.auth.password`.
```yaml
image:
tag: "v4.3.10"
pullSecrets:
- name: softprobe-gcr-pull
mongodb:
connectionString: "mongodb://USER:PASS@mongo.host:27017/your_release_sp_storage_db?authSource=admin"
encryption:
enabled: true
secretKey: "CHANGE_ME_base64_32_byte_key"
```
### 4. Helm install
Use the chart version and image tag from your Softprobe release (`v4.3.10` → chart `4.3.10`, image `v4.3.10`).
```bash
helm install softprobe softprobe/sp-backend \
--version 4.3.10 \
--namespace softprobe \
-f values.yaml \
--set image.tag=v4.3.10 \
--set createNamespace=false
```
## Verify
```bash
kubectl get pods -n softprobe
kubectl port-forward -n softprobe svc/softprobe-sp-backend 8090:8090
curl -s http://127.0.0.1:8090/actuator/health
```
**Bundled mode:** expect pods for `mongodb`, `redis`, `sp-backend`, and (chart v4.3.x+) `log-vector`.
**External mode:** expect `redis`, `sp-backend`, and `log-vector` (no `{release}-mongo` pod).
## Upgrade an existing release
Use the same release name, namespace, and `values.yaml` you used at install. A Softprobe release tag maps to Helm as:
| Release tag | Chart `--version` | `image.tag` |
|-------------|-------------------|-------------|
| `v4.3.10` | `4.3.10` | `v4.3.10` |
### Before you upgrade
1. **Keep your existing `values.yaml`** — you do not need to replace it. Helm merges your file with the new chart defaults for any key you omitted.
2. **Older file without `logPipeline`?** If you installed on v4.3.5 or earlier with only `image`, `mongodb`, and `encryption`, bump `--version` and `image.tag` only. Missing keys inherit chart defaults — **`logPipeline.enabled` is `true`**, so Vector, the Parquet PVC (local mode), compaction, and retention are added on upgrade. Use `--dry-run` first to preview new resources.
3. **Review optional overrides** — download the current [values.example.yaml](https://storage.googleapis.com/softprobe-published-files/helm/sp-backend/latest/values.example.yaml) and merge only what you need (PVC `storageClass`, `placement`, S3 backend). Do **not** change `encryption.secretKey` — existing encrypted payloads depend on it.
4. **Keep your MongoDB mode** — do not switch between bundled and external MongoDB on upgrade.
5. **Confirm registry access** — the `softprobe-gcr-pull` secret must still be valid for the new `image.tag`.
6. **Preview the diff** (optional):
```bash
helm repo update
helm upgrade softprobe softprobe/sp-backend \
--version 4.3.10 \
-n softprobe \
-f values.yaml \
--set image.tag=v4.3.10 \
--set createNamespace=false \
--dry-run
```
### Upgrade from the Helm repo
Typical upgrade — same `values.yaml` as install, new chart and image version:
```bash
helm repo update
helm upgrade softprobe softprobe/sp-backend \
--version 4.3.10 \
-n softprobe \
-f values.yaml \
--set image.tag=v4.3.10 \
--set createNamespace=false
```
Replace `softprobe` with your release name if different. Pin **`image.tag`** to the semver release Softprobe gave you — not `latest`.
**Example — old values file, no `logPipeline` block:** your file still looks like this:
```yaml
image:
tag: "v4.3.5"
pullSecrets:
- name: softprobe-gcr-pull
mongodb:
bundled:
auth:
password: "your-existing-password"
encryption:
enabled: true
secretKey: "your-existing-key"
```
Upgrade command (no edits required):
```bash
helm upgrade softprobe softprobe/sp-backend \
--version 4.3.10 \
-n softprobe \
-f values.yaml \
--set image.tag=v4.3.10 \
--set createNamespace=false
```
Helm adds log-pipeline resources from chart defaults. After rollout, instrument workloads with `-Dsp.api.url` pointing at sp-backend (see [Agent log export](#agent-log-export)).
### Upgrade from a downloaded chart package
If you install offline or verify SHA-256 from GCS:
```bash
curl -fLO "https://storage.googleapis.com/softprobe-published-files/helm/sp-backend/v4.3.10/sp-backend-4.3.10.tgz"
helm upgrade softprobe ./sp-backend-4.3.10.tgz \
-n softprobe \
-f values.yaml \
--set image.tag=v4.3.10 \
--set createNamespace=false
```
### Verify after upgrade
```bash
kubectl rollout status -n softprobe deploy/softprobe-sp-backend
kubectl get pods -n softprobe
kubectl get pods,cronjob,pvc -n softprobe | grep -E 'log-vector|log-parquet|compaction|retention'
kubectl port-forward -n softprobe svc/softprobe-sp-backend 8090:8090
curl -s http://127.0.0.1:8090/actuator/health
```
Expect a rolling restart of `sp-backend` (and Redis if the chart template changed). Bundled MongoDB data on the existing PVC is preserved. `sp-backend` may take up to ~2 minutes to become ready after the new pod starts (JVM warm-up).
On v4.3.10+ you should also see `log-vector` and a `log-parquet` PVC (local storage). Instrument workloads with:
```text
-Dsp.api.url=http://-sp-backend..svc.cluster.local:8090
```
Log export uses `{sp.api.url}/v1/logs`; sp-backend proxies to Vector internally.
### Customize or disable the log pipeline
The pipeline is **on by default**. Merge overrides from [values.example.yaml](https://storage.googleapis.com/softprobe-published-files/helm/sp-backend/latest/values.example.yaml) only when you need non-default storage, placement, or S3:
```yaml
logPipeline:
parquet:
storageSize: 100Gi
storageClass: managed-csi
```
To **disable** on upgrade, add before running `helm upgrade`:
```yaml
logPipeline:
enabled: false
```
Full options: [Unified log pipeline](#unified-log-pipeline).
### Upgrade troubleshooting
| Symptom | Check |
|---------|--------|
| `ImagePullBackOff` after upgrade | New `image.tag` exists in GCR; `softprobe-gcr-pull` secret valid |
| `helm upgrade` fails on MongoDB | Still set **one** of `mongodb.connectionString` or `mongodb.bundled.auth.password` — do not clear both |
| New log Parquet PVC pending | Cluster `StorageClass` — set `logPipeline.parquet.storageClass` |
| Log query empty after enabling pipeline | Agent OTLP endpoint and trace bounds — see [log pipeline troubleshooting](#troubleshooting) |
## Unified log pipeline {#unified-log-pipeline}
Enable correlated log ingest, Parquet storage, and trace-id query (`sp logs` / `GET /api/recorder/logs`) from the **sp-backend** Helm chart.
**Prerequisites:** a healthy `sp-backend` release on chart **v4.3.x+**. The pipeline is **enabled by default** (`logPipeline.enabled: true`). Instrumented workloads need `-Dsp.api.url` pointing at sp-backend (see [Agent log export](#agent-log-export)).
### What the chart deploys
When `logPipeline.enabled: true`, Helm adds:
| Resource | Purpose |
|----------|---------|
| **Vector** (`{release}-log-vector`) | OTLP log ingest (gRPC/HTTP + agent JSON on `:4320`) |
| **rclone gateway** (`{release}-log-rclone`, local + Azure Blob) | Shared S3 gateway Deployment + Service that Vector and sp-backend use to write/read Parquet |
| **Parquet PVC** (local mode only) | Durable storage mounted only by the rclone gateway; all other pods reach it via the gateway's S3 endpoint |
| **Compaction CronJob** (all backends) | Merges closed-hour minute files → `part-hourly.parquet` (DuckDB) |
| **Retention CronJob** (optional, all backends) | Prunes Parquet older than `ttlDays` |
sp-backend is wired for Parquet reads and exports its own diagnostic logs when the pipeline is enabled (`OTEL_ENABLED=true`, `OTEL_LOGS_EXPORTER=otlp-filtered`).
### Configure in Helm
The chart enables the pipeline by default. Override in your values file (or `--set` on install/upgrade) when you need non-default storage, placement, or to disable:
```yaml
logPipeline:
enabled: true # default; set false to disable
storage:
backend: local # local | s3 | azure_blob — see "Choose where logs are stored"
parquet:
storageSize: 100Gi
# storageClass: managed-csi # optional — cluster default if omitted
retention:
ttlDays: 4 # set "" to disable retention CronJob
cleanupSchedule: "0 3 * * *"
compaction:
enabled: true # local storage only
schedule: "15 * * * *" # previous closed UTC hour
# Pin Vector + maintenance jobs to the same node pool as sp-backend when using taints:
placement:
nodeSelector:
workload: softprobe-backend
tolerations:
- key: workload
operator: Equal
value: backend
effect: NoSchedule
```
**Upgrading from an older `values.yaml` without a `logPipeline` block?** You do not need to add one — chart defaults apply and the pipeline is deployed on upgrade. See [Upgrade an existing release](#upgrade-an-existing-release).
Example upgrade with explicit overrides (optional):
```bash
helm upgrade softprobe softprobe/sp-backend \
--version 4.3.10 \
-n softprobe \
-f values.yaml \
--set image.tag=v4.3.10 \
--set createNamespace=false
```
Pin **`image.tag`** to a semver release (for example `v4.3.10`), not `latest`, so the backend matches your chart version.
### Verify log pipeline
```bash
kubectl get pods,cronjob,pvc -n softprobe | grep -E 'log-vector|log-parquet|compaction|retention'
kubectl port-forward -n softprobe svc/softprobe-sp-backend 8090:8090
```
Run a canned lookup (replace trace id and bounds):
```bash
export SP_API_URL=http://127.0.0.1:8090
sp logs --trace-id <32-hex> --since 2026-06-27T10:00:00Z --until 2026-06-27T10:05:00Z
```
Or:
```bash
curl -s "$SP_API_URL/api/recorder/logs?trace_id=&since=2026-06-27T10:00:00Z&until=2026-06-27T10:05:00Z"
```
v1 has **no** dedicated pipeline health API — a successful trace-id lookup confirms ingest, storage, and query wiring.
The same Vector Deployment also stores Softprobe **metrics** under a sibling `metrics/` Parquet dataset. See [Metrics data plane](./metrics-data-plane) for `POST /v1/metrics` and `GET /api/recorder/metrics`.
### Agent log export {#agent-log-export}
Point the Java agent at sp-backend only:
```text
-Dsp.api.url=http://-sp-backend..svc.cluster.local:8090
```
For release `softprobe` in namespace `softprobe`:
```text
-Dsp.api.url=http://softprobe-sp-backend.softprobe.svc.cluster.local:8090
```
Correlated application and agent logs export to `{sp.api.url}/v1/logs` during record and replay. sp-backend proxies agent JSON to Vector `:4320` and OTLP to `:4318` when the log pipeline is enabled.
Optional advanced override — direct Vector ingest (bypasses backend proxy):
```text
-Dsp.otel.exporter.otlp.log.endpoint=http://-log-vector..svc.cluster.local:4320/v1/logs
```
Legacy capture flags (`sp.record.user.log`, `sp-capture-log`, `sp.user.log.level`, etc.) are not used in v1.
### Choose where logs are stored
Set `logPipeline.storage.backend` to pick where Parquet log files live. Pick one:
| Backend | `logPipeline.storage.backend` | Best for | Credentials you provide |
|---------|-------------------------------|----------|-------------------------|
| **Local disk** (default) | `local` | Single cluster, simplest setup | None (in-cluster volume) |
| **S3-compatible** | `s3` | AWS S3, MinIO, GCS, any S3 API | `access-key-id` + `secret-access-key` |
| **Azure Blob** | `azure_blob` | Azure Storage accounts | `account-key` |
Querying, compaction, and retention behave identically across all three — only the storage location and credentials differ.
#### Option 1 — Local disk (default)
Nothing to set up. The chart creates a PersistentVolumeClaim and stores Parquet there. Adjust size/class only if needed:
```yaml
logPipeline:
storage:
backend: local
parquet:
storageSize: 100Gi
storageClass: "" # cluster default; or e.g. managed-csi (AKS), gp3 (EKS)
```
Back up by snapshotting the `{release}-log-parquet` PVC.
#### Option 2 — S3-compatible bucket
**Step 1 — create the credentials secret.** It must contain exactly these two keys:
```bash
kubectl create secret generic softprobe-log-s3-credentials -n softprobe \
--from-literal=access-key-id='AKIA...' \
--from-literal=secret-access-key='...'
```
| Secret key | Value |
|------------|-------|
| `access-key-id` | Bucket access key ID |
| `secret-access-key` | Bucket secret access key |
**Step 2 — point the chart at your bucket and that secret:**
```yaml
logPipeline:
storage:
backend: s3
s3:
bucket: my-softprobe-logs
endpoint: https://s3.amazonaws.com # or your MinIO / GCS / other S3 endpoint
region: us-east-1
forcePathStyle: true # keep true for MinIO
prefix: "" # optional key prefix inside the bucket
existingSecret: softprobe-log-s3-credentials
```
The bucket must already exist. No Parquet PVC is created in this mode.
#### Option 3 — Azure Blob container
**Step 1 — create the credentials secret.** It must contain exactly one key, `account-key`, holding your storage account access key:
```bash
kubectl create secret generic softprobe-log-azure-credentials -n softprobe \
--from-literal=account-key=''
```
| Secret key | Value |
|------------|-------|
| `account-key` | Storage account access key (Azure portal → **Storage account → Security + networking → Access keys**, or `az storage account keys list --account-name --query '[0].value' -o tsv`) |
**Step 2 — point the chart at your container and that secret:**
```yaml
logPipeline:
storage:
backend: azure_blob
azureBlob:
container: my-softprobe-logs
accountName:
endpoint: https://.blob.core.windows.net
prefix: "" # optional prefix inside the container
existingSecret: softprobe-log-azure-credentials
```
The container must already exist. Softprobe authenticates with the account name + access key (Azure Shared Key). No Parquet PVC is created in this mode.
#### Compaction & retention (all backends)
Both run automatically and identically for `local`, `s3`, and `azure_blob` — no cloud lifecycle rules required:
* **Compaction** (`logPipeline.compaction`, hourly, on by default): merges each closed UTC hour's minute files into a single `part-hourly.parquet`.
* **Retention** (`logPipeline.retention.ttlDays`, default `4`): deletes Parquet older than N days. Set `ttlDays: ""` to keep logs forever.
> Compaction uses the `softprobe/duckdb:1.1.3` image (`linux/amd64`). On Apple Silicon dev clusters, build/load an `arm64` build (`make duckdb-image DUCKDB_PLATFORM=linux/arm64`) and override `logPipeline.compaction.image`.
**Security:** end users and Agent Skills **must never** receive bucket or storage-account credentials — they query only through `sp logs` / `GET /api/recorder/logs`.
### Helm values reference
| Value | Description |
|-------|-------------|
| `logPipeline.enabled` | Deploy Vector, storage, and query wiring (default `true`) |
| `logPipeline.storage.backend` | `local` (PVC), `s3`, or `azure_blob` |
| `logPipeline.parquet.storageSize` / `storageClass` | Local Parquet PVC size and class |
| `logPipeline.vector.image` | Vector image (default `timberio/vector:0.56.0-debian`) |
| `logPipeline.vector.resources` | CPU/memory for the log collector |
| `logPipeline.otlp.httpPort` / `grpcPort` / `agentJsonPort` | OTLP ports (defaults `4318` / `4317` / `4320`) |
| `logPipeline.retention.ttlDays` | Prune TTL in days; `""` disables retention CronJob |
| `logPipeline.retention.cleanupSchedule` | Retention CronJob schedule (default `0 3 * * *`) |
| `logPipeline.compaction.enabled` / `schedule` / `image` | Hourly compaction for every backend (default on, `15 * * * *`, `softprobe/duckdb:1.1.3`) |
| `logPipeline.placement` | `nodeSelector` / `tolerations` / `affinity` for Vector and maintenance CronJobs |
| `logPipeline.agentLogEndpointProperty` | Documented JVM property: `sp.otel.exporter.otlp.log.endpoint` |
`logPipeline.parquet.localRoot` exists in chart defaults (`/data/parquet/logs`) and must stay aligned with the rclone bucket layout — operators normally **do not** override it.
### Maintenance jobs
| CronJob | When | What |
|---------|------|------|
| `{release}-log-vector-retention` | `ttlDays` set (any backend) | AWS CLI deletes Parquet objects older than TTL over the S3 endpoint (native bucket, or the rclone gateway for local/Azure Blob) |
| `{release}-log-vector-compaction` | `compaction.enabled` (any backend) | DuckDB merges previous hour's `part-*.parquet` → `part-hourly.parquet` over the S3 endpoint, then AWS CLI prunes the minute objects |
Check last run:
```bash
kubectl get cronjob,jobs -n softprobe -l 'app.kubernetes.io/component=log-pipeline-maintenance'
kubectl logs -n softprobe job/
```
### Out of scope (v1)
* Iceberg, ad hoc SQL, direct Parquet access for end users.
* Dual-write to local PVC and S3.
* Dedicated log-pipeline health/status API.
## Uninstall
```bash
helm uninstall softprobe -n softprobe
```
Bundled MongoDB PVCs are retained by default. Delete manually if required:
```bash
kubectl delete pvc -n softprobe -l app.kubernetes.io/instance=softprobe
```
## Java agents
Point instrumented applications at the in-cluster service:
```text
-Dsp.api.url=http://softprobe-sp-backend.softprobe.svc.cluster.local:8090
```
## Troubleshooting
| Symptom | Check |
|---------|--------|
| `helm install` fails on MongoDB | Set **one** of `mongodb.connectionString` or `mongodb.bundled.auth.password` |
| `sp-backend` pod `Init:0/1` (bundled) | MongoDB or Redis not ready — `kubectl get pods -n softprobe` |
| `sp-backend` slow start | JVM warm-up — up to ~2 minutes (startup probe) |
| `ImagePullBackOff` | Missing `softprobe-gcr-pull` secret or wrong `image.tag` |
| Mongo PVC pending (bundled) | No StorageClass — set `mongodb.bundled.storageClass` |
| External MongoDB connection errors | URI reachable from cluster; unique DB name; correct `authSource` |
| Empty `GET /api/recorder/logs` but data expected | Partial `part-hourly.parquet` from interrupted compaction — delete hourly file or wait for next compaction; confirm minute `part-*.parquet` files exist |
| Vector pod not ready | `kubectl logs -n softprobe deploy/-log-vector -c vector` |
| Compaction `ImagePullBackOff` on arm64 | Override `logPipeline.compaction.image` with a local `arm64` build |
| Agent logs missing | `sp.api.url` must reach sp-backend; log pipeline enabled; trace must have `trace_id` on export |
| sp-backend logs missing | `logPipeline.enabled` auto-enables OTLP export on sp-backend |
## Next step
After sp-backend is healthy, install Softprobe on developer machines: [Install Softprobe Client](./).
For a shared team web workbench on Linux, see [Spcode Service](./index#spcode-service) on the client install page.
Related: [`sp logs`](/en/testing/commands/logs) · [Log correlation IDs](/en/testing/reference/log-correlation-ids)
---
---
url: /en/testing/installation/metrics-data-plane.md
---
# Metrics data plane
Softprobe metrics use the **same collector and Parquet store family as logs**: OTLP → Vector → one-minute aggregate → Parquet under a `metrics/` dataset → product HTTP query. This is for **ad-hoc diagnosis** (for example “are we receiving agent logs?”), not Prometheus/Grafana dashboards.
Requires chart **v4.3.x+** with the [unified log pipeline](./server#unified-log-pipeline) enabled (default). Metrics reuse that Vector Deployment — no second time-series database and no extra pods.
## Ingest — `POST /v1/metrics`
Clients POST OpenTelemetry metrics to Softprobe. sp-backend proxies to Vector when the telemetry pipeline is ready.
| Content-Type | Payload |
|--------------|---------|
| `application/x-protobuf` | OTLP `ExportMetricsServiceRequest` |
| `application/json` | OTLP metrics JSON (`resourceMetrics` …) |
**Accepted formats:** OTLP protobuf and OTLP JSON only. Softprobe does **not** accept a flat-JSON metrics dialect on this endpoint. OTLP JSON is accepted at Softprobe and forwarded to Vector as protobuf.
| Pipeline state | Response |
|----------------|----------|
| Ready | `200` after accept/proxy |
| Not ready | `503` — Softprobe does **not** return success while dropping data |
Example (OTLP JSON):
```bash
curl -sS -X POST "$SP_API_URL/v1/metrics" \
-H 'Content-Type: application/json' \
--data-binary @export-metrics.json
```
Instrumented backends also **emit** catalog counters in-process to the same Vector metrics path (no HTTP self-POST).
## Query — `GET /api/recorder/metrics`
Bounded, read-only lookups over Softprobe Parquet under `metrics/`.
**Required query parameters:**
| Parameter | Description |
|-----------|-------------|
| `metric_name` | Metric name to select |
| `since` | ISO-8601 UTC inclusive |
| `until` | ISO-8601 UTC exclusive — window is `[since, until)` |
Optional exact-match filters on promoted label columns only: `status`, `kind`, `source`, `result`, `content_type`. High-cardinality selectors (`trace_id`, full URL, exception message) and non-catalog keys such as `app_id` are rejected.
There is **no** product row `limit` and **no** max time-span beyond requiring valid bounds (same spirit as log query). Missing partitions return HTTP 200 with empty `rows`.
```bash
curl -sS "$SP_API_URL/api/recorder/metrics?metric_name=sp.logs.ingest.requests&since=2026-07-10T18:00:00Z&until=2026-07-10T18:05:00Z"
```
Example success shape:
```json
{
"lookup": {
"metric_name": "sp.logs.ingest.requests",
"windows": [{ "since": "2026-07-10T18:00:00Z", "until": "2026-07-10T18:05:00Z" }],
"filters": {}
},
"rows": [
{
"timestamp": "2026-07-10T18:01:00Z",
"metric_name": "sp.logs.ingest.requests",
"metric_type": "sum",
"service_name": "sp-backend",
"value": 3,
"status": "ok",
"kind": "agent_json"
}
],
"warnings": []
}
```
Error bodies must not expose Parquet paths, bucket names, or storage credentials.
## Backend P0 catalog (R1)
Softprobe emits these series from the log ingest/forward path:
| Name | Meaning |
|------|---------|
| `sp.logs.ingest.requests` | Every `POST /v1/logs` attempt (`status`, `kind`) |
| `sp.logs.ingest.records` | Records after convert (`kind`, `source`) |
| `sp.logs.forward.results` | Export outcome (`success` / `failure` / `skipped_*`) |
| `sp.logs.forward.duration_ms` | Export timing histogram |
After Vector’s one-minute aggregate, expect queryable rows within about a minute (CI polls up to **70 seconds**).
## Out of scope
* Prometheus scrape, Grafana, or PromQL as the Softprobe product path
* `sp metrics` CLI (HTTP API is the R1 contract)
* Agent-side `sp.agent.logs.*` emitters (later round)
* Direct Parquet or storage credentials for end users
---
---
url: /en/testing/installation.md
---
# Install Softprobe Client
::: tip Prerequisite
Deploy the Softprobe backend on your cluster first: [Install Softprobe Server](./server).
:::
Install Softprobe with the global installer:
```bash
curl -fsSL https://install.softprobe.ai/install.sh | bash
```
The default install adds the Softprobe CLI (`sp`), the Java agent, and the web UI used for testing.
After install, confirm `sp` is available:
```bash
sp -v
```
If that prints a version, you are ready for the steps below.
## Setup
`sp setup` configures the self-hosted or on-prem Softprobe API URL.
```bash
sp setup
```
If you omit `--api-url`, the wizard **prompts** for your API URL. On Linux it may also ask whether to install Spcode Service ([below](#spcode-service)).
| Flag | Description |
|------|-------------|
| `--api-url` | API URL (use this to skip the prompt) |
Model provider configuration belongs to `sp code`, not `sp setup`.
For non-interactive or scripted setup:
```bash
sp setup --api-url http://127.0.0.1:8090 --json
```
The URL is stored in Softprobe config at `~/.config/softprobe/config.jsonc`.
## Spcode Service (Linux only) {#spcode-service}
On **Linux**, `sp setup` can optionally install **Spcode Service** — a shared web workbench your team opens in the browser on the corp network. For a private UI on your own machine, see [Launch Softprobe Web UI Manually](./code) instead.
```bash
sp setup --api-url http://sp-backend.corp:8090 --install-spcode-service
sp setup --install-spcode-service --spcode-service-port 5000
sp setup --uninstall-spcode-service
```
| Flag | Description |
|------|-------------|
| `--install-spcode-service` | Install the shared web workbench (**Linux only**) |
| `--uninstall-spcode-service` | Stop and remove Spcode Service |
| `--spcode-service-port` | Listen port when installing (default `4096`) |
`--install-spcode-service` and `--uninstall-spcode-service` cannot be combined.
### Install
```bash
# Interactive: API URL → opt-in → port (default 4096)
sp setup
# Non-interactive
sp setup --api-url http://sp-backend.corp:8090 --install-spcode-service
# Automation without TTY
sudo sp setup --api-url http://sp-backend.corp:8090 --install-spcode-service
```
Install Softprobe and run `sp setup` as the account that should own the machine’s Softprobe settings. Spcode Service uses those same settings (backend URL, MCP, agent instructions, skills). After you change them, restart the service.
### Operations
| Task | Command |
|------|---------|
| Status | `systemctl status spcode-web.service` |
| Stop / start | `systemctl stop spcode-web.service` / `systemctl start spcode-web.service` |
| Logs | `journalctl -u spcode-web -n 50 --no-pager` |
If start fails, inspect `journalctl -u spcode-web` before retrying.
Optional MCP tools, agent instructions, and skills: [Client configuration](./configuration).
### Uninstall
```bash
sp setup --uninstall-spcode-service
```
Removes the systemd unit. Softprobe config files under your install account remain unless you delete them.
## Next
The CLI is ready — now bring your Java application in:
1. [Supported frameworks](/en/testing/supported-frameworks) — confirm your stack can be instrumented
2. [Attach the Java agent](/en/testing/java-agent) — attach the agent and register your app
3. [Launch the Web UI](./code) — view recordings and replays in the browser
Tools and advanced: [Doctor](./doctor) (troubleshooting) · [Upgrade](./upgrade) · [Client configuration](./configuration) (MCP, `AGENTS.md`, skills)
---
---
url: /en/testing/supported-frameworks.md
---
# Supported frameworks (Java)
Softprobe Testing instruments common Java stacks through the agent’s module plugins. The list below reflects currently documented support; new integrations are added continuously in [`sp-agent-java`](https://github.com/softprobe/softprobe/tree/main/backend/sp-agent-java).
::: tip
If your stack is not listed, check whether it uses a supported client (for example any JDBC layer backed by MyBatis or Hibernate). Entry points must be one of the HTTP/RPC provider modules the agent weaves.
:::
## Foundation
* Java Executors
* System time (`DynamicClass` — force-mocked on replay by default policy)
* User-configured dynamic types
## Cache
* Caffeine Cache
* Guava Cache
* Spring Cache
## Spring / HTTP entry
* Spring Boot 1.4+, 2.x+
* Servlet API 3+, 5+
## HTTP clients
* Apache HttpClient 4.0+
* OkHttp 3.0 – 4.11
* Spring WebClient 5.0+
* Spring `RestTemplate`
* Feign 9.0+
* Elasticsearch Client 7.x
## Redis
* `RedisTemplate`
* Jedis 2.10+, 4+
* Redisson 3.0+
* Lettuce 5.x, 6.x
## Persistence
* MyBatis 3.x, MyBatis-Plus, TkMyBatis
* Hibernate 5.x
## NoSQL
* MongoDB driver 3.x, 4.x
## RPC
* Apache Dubbo 2.x, 3.x
* Alibaba Dubbo 2.x
* SOFA RPC (provider/consumer categories in backend model)
## Auth
* Spring Security 5.x
* Apache Shiro 1.x
* jCasbin 1.x
* Auth0 JWT 3.x
* JJWT 0.1+, jjwt-api 0.10+
## Netty
* Netty server 3.x, 4.x
## Config
* Apollo Config 1.x, 2.x
## Backend dependency categories
Replay matching uses **dependency** categories (not entry types) in mock policy. Common names aligned with [`MockCategoryType`](https://github.com/softprobe/softprobe):
| Category | Typical use |
|----------|-------------|
| `HttpClient` | Outbound HTTP |
| `Database` | JDBC / ORM SQL |
| `Redis` | Redis commands |
| `DubboConsumer` / `SofaConsumer` | RPC client |
| `QMessageProducer` | Message publish |
| `DynamicClass` | Time, random, cache, encryption helpers |
| `ConfigFile` | Config reads (often skipped in compare) |
Entry categories (`Servlet`, `DubboProvider`, …) define what was recorded as the **main** API under test.
## Related
* [Java agent](/en/testing/java-agent)
* [Mock policy](/en/testing/policies#mock-policy)
* [How it works](/en/testing/how-it-works)
---
---
url: /en/testing/download-java-agent.md
---
# Download Java agent
Download the latest Java agent:
```bash
curl -fsSL -o sp-agent.jar https://install.softprobe.ai/artifacts/agent/latest/sp-agent.jar
```
Pin a specific release by replacing `latest` with a version:
```bash
curl -fsSL -o sp-agent.jar https://install.softprobe.ai/artifacts/agent/v4.3.9/sp-agent.jar
```
## Available versions
The mutable `latest` alias always points at the current Java agent. Immutable releases are listed below from `install.softprobe.ai`.
Scriptable version list:
```bash
curl -fsSL https://install.softprobe.ai/artifacts/agent/versions.json
```
## Next
Attach the agent and configure JVM flags: [Attach and configure](/en/testing/java-agent).
---
---
url: /en/testing/java-agent.md
---
# Softprobe Java agent
The Softprobe Java agent (`sp-agent.jar`) attaches to your JVM with `-javaagent`. It instruments frameworks at bytecode level (similar in *deployment* to an OpenTelemetry Java agent) but its purpose is **test data capture and replay-time mocking**, not generic distributed tracing.
::: warning Not the Istio/Envoy agent
Mesh capture is documented under [Platform agent architecture](/en/platform/advanced-guides/agent-architecture). This page covers the **JVM** agent only.
:::
## Prerequisites
* Java service you can restart with JVM flags
* **sp-backend** reachable from the agent host (default `http://127.0.0.1:8090` locally)
* Registered **`appId`** — create with `sp app create` and pin the same id on every instance
## Startup command
Attach the agent with `-javaagent` and the JVM properties below:
```bash
java \
-javaagent:sp-agent.jar \
-Dsp.app.id= \
-Dsp.api.url=http://127.0.0.1:8090 \
-jar your-service.jar
```
The agent may also resolve an app id automatically from jar name or environment; explicit `-Dsp.app.id` avoids mismatches between record and replay. Legacy docs and some configs still use **`sp.service.name`** — treat it as an alias in older deployments; prefer **`sp.app.id`** for new setups.
| Property | Points to | Purpose |
|----------|-----------|---------|
| `-Dsp.app.id` | — | Registered application id (16-char hex from `sp app create`). **Pin this** in every environment that shares recordings. |
| `-Dsp.api.url` | **sp-backend** (e.g. `:8090`) | **Required** — sp-backend base URL (must include `http://` or `https://`). Env fallback: `SP_API_URL`. Record, replay, mock, compare, **and correlated log export** (`{sp.api.url}/v1/logs`). |
When `sp.api.url` is set and the server [unified log pipeline](./installation/server.md#unified-log-pipeline) is enabled, logs are proxied to Vector internally — you do **not** need a separate Vector URL on the agent.
### Optional: direct Vector override
For advanced setups (bypassing the backend proxy), set:
```bash
-Dsp.otel.exporter.otlp.log.endpoint=http://:4320/v1/logs
```
This JVM property wins over `{sp.api.url}/v1/logs`.
Without `sp.api.url` (and without the override above), record and replay still work, but application logs are not exported and `sp logs` will be empty for that trace.
## Environment tags
Tag recorded traffic for filtering and replay scope:
```bash
-Dsp.mocker.tags=env=staging
```
Recorded mockers carry `env=` so you can replay only traffic from a given environment. Match the same tag in a policy via `selector.envTags` — see [Policy YAML guide · Common fields](/en/testing/policy-yaml-guide#common-fields).
## Alternative deployment patterns
### `sp.agent.conf` file
```properties title="META-INF/sp/sp.agent.conf (baked into agent JAR)"
sp.api.url=http://127.0.0.1:8090
```
All-in-one and Helm installs bake this at packaging time so operators only need `-javaagent:sp-agent.jar` and `-Dsp.app.id`. Override with `SP_API_URL` or `-Dsp.api.url` when redirecting to another backend.
### Tomcat / `JAVA_OPTS`
Set agent flags in `catalina.sh` or `JAVA_TOOL_OPTIONS` so every worker JVM loads the agent on startup.
### Coexistence with OpenTelemetry
If another `-javaagent` conflicts (for example OpenTelemetry), add ignore prefixes:
```bash
-Dsp.ignore.type.prefixes=io.opentelemetry
-Dsp.ignore.classloader.prefixes=io.opentelemetry
```
Comma-separate multiple prefixes.
### Debug logging
```bash
-Dsp.enable.debug=true
```
## Agent status
`sp app status ` reports **`online`**, **`offline`**, or **`never`** from instance heartbeats (default offline threshold ~60 seconds). Status reflects running agents, not merely app registration.
During recording, legacy UIs showed **WORKING** / **SLEEPING** / **UNSTART** per instance; the same idea applies: the agent must be injected and recording enabled to produce cases.
## What a complete case looks like
A healthy recorded case typically includes:
* **Servlet** (or other entry type) — main API request/response
* **Database**, **Redis**, **HttpClient**, … — dependency mockers in call order
* **DynamicClass** — optional, for configured cache/time/encryption methods
List cases after traffic: `sp record case list --app --json`.
## Production safety
To limit impact on live traffic, the agent implements **backpressure** when overloaded or when storage is unhealthy.
### Queue overflow
Recording tasks enter an in-memory queue (default capacity **1024**).
2\. If the queue is full, recording stops immediately.
3\. After ~30s, a health task lowers sampling (~20%) and retries.
4\. If still full after ~5 minutes, frequency drops again until a minimum (~once per hour).
5\. When the queue recovers (~10 minutes later), normal recording resumes.
### Storage health
1. If sp-storage calls fail or time out, recording stops immediately.
2. After ~10s, recording resumes while health is sampled.
3. If metrics stay unhealthy for ~3 minutes, frequency is reduced in steps like queue overflow until storage recovers.
Combined with [recording policy](/en/testing/policies) sampling and desensitization, this keeps production risk bounded.
## Replay-side agent
The **same** agent JAR must be attached on the instance that receives replay traffic. Set recording to minimal or zero on dedicated replay hosts so you only mock, not capture new production-like volume unintentionally.
## Next
Agent attached and `sp app status` shows online? Onboarding is done → head into the core workflow with **[Record traffic](/en/testing/recording)**.
Related: [Download Java agent](/en/testing/download-java-agent) · [Supported frameworks](/en/testing/supported-frameworks) · [Getting started](/en/testing/getting-started)
---
---
url: /en/testing/installation/code.md
---
# Launch Softprobe Web UI Manually
Most testing work happens in the **Softprobe web UI** in your browser — record, replay, and inspect traces there.
This page is for starting that UI **on your own dev machine** when you want a private session on your laptop or workstation. It is not the same as a **shared web workbench** that your team opens from one URL on the network; that optional server install is covered under [Spcode Service](./#spcode-service) on the install page.
## Start the UI on your dev box
```bash
sp code web --port 4096
```
Then open `http://127.0.0.1:4096` (or the host and port you chose). The UI uses **your** Softprobe config and backend URL from `sp setup`.
You can also open the terminal coding experience without the browser:
```bash
sp code
```
If Softprobe cannot start the web UI, run `sp doctor` and follow the remediation, or run `sp upgrade` if the install is out of date.
## Dev machine vs shared workbench
| | On your dev box (this page) | Shared web workbench (optional) |
|--|-------------------------------|----------------------------------|
| **Who** | You, on your machine | Your organization, one URL for the team |
| **Typical use** | Local testing while you develop | Colleagues in the same network open the same workbench |
| **How** | `sp code web` in your shell | `sp setup --install-spcode-service` during install ([Spcode Service](./#spcode-service)) |
| **Config** | Your Softprobe settings from `sp setup` | Same Softprobe settings as the account that installed Spcode Service |
Use the dev-box flow when you are the only person on that machine. Choose the shared workbench when several people need browser access without each running their own process.
## Next
With the UI opening, your onboarding toolkit is complete. Haven't run the full loop yet? Walk through [Getting started](/en/testing/getting-started); if you already have recorded cases, this UI is where you'll [Review diffs](/en/testing/review-diffs-in-the-web-ui) after a replay.
---
---
url: /en/testing/installation/doctor.md
---
# Doctor
Use `sp doctor` to check a self-hosted Softprobe installation:
```bash
sp doctor
sp doctor --json
```
The doctor surface reports backend reachability and internal coding engine installation with remediation for failures.
When **`spcode-web.service`** is registered (Spcode Service installed via [`sp setup --install-spcode-service`](/en/testing/installation/#spcode-service)), `sp doctor` also checks that the service is healthy and can reach the configured Softprobe backend.
---
---
url: /en/testing/installation/upgrade.md
---
# Upgrade
Upgrade Softprobe with:
```bash
sp upgrade
```
The command uses the global installer lifecycle so the CLI, Java agent, and internal coding engine update as one Softprobe install. Partial component failures are reported visibly.
If **Spcode Service** is installed (`spcode-web.service`), restart after upgrading `sp` or `spcode`:
```bash
sudo systemctl restart spcode-web.service
journalctl -u spcode-web -n 20 --no-pager
```
---
---
url: /en/testing/recording.md
---
# Record traffic
Recording means letting your agent-attached application **handle real requests as usual** — every request that flows through, together with the dependency calls it triggers (database, HTTP, Redis, …), is automatically stored as a **case**: the raw material for replay.
This page follows `order-service` as the running example: the agent is attached per [Attach the Java agent](/en/testing/java-agent), and the app is registered (you have its `appId`) as in the quick start. All four workflow steps use this same application.
::: tip Records out of the box — no policy needed first
The built-in global default policy (priority 0) makes recording work out of the box, and already excludes `/health` and similar probes. You only need to write an app-level policy when you want to tune sampling, time windows, or operation scope — see [Tune what gets recorded](#tune-what-gets-recorded) at the end of this page.
:::
## Step 1 · Confirm the agent is online
Start the application in the **environment whose traffic you want to capture** (usually production or staging):
```bash
java -javaagent:sp-agent.jar \
-Dsp.app.id= \
-Dsp.api.url=http://:8090 \
-jar order-service.jar
```
Confirm the agent has reported in:
```bash
sp app status --json
```
In multi-environment deployments, tag instances (e.g. `-Dsp.mocker.tags=env=prod`) — you will use the same tags later to filter cases and match policies.
## Step 2 · Let real traffic flow
Nothing to do — user requests, business calls, and load-test traffic passing through the app are captured automatically. In environments without natural traffic (e.g. staging), send a few business requests to the APIs yourself.
::: tip Cases are recorded, never hand-written
The CLI cannot author cases manually. Want more cases? Send more traffic through the app.
:::
## Step 3 · Confirm cases exist
```bash
sp record case list --app --since -1h --json
```
Cases showing up in the list means step 1 of the workflow is done — move on to [Replay](/en/testing/replay-and-diff).
To check completeness per trace, use `sp record completeness --json`.
## No cases? Check this table
| Symptom | Check first |
|---------|-------------|
| Empty `sp record case list` | An app-level policy set `ratePerHundredSeconds` to 0; outside `timeWindow`; operation `exclude`d |
| Agent shows not recording | `machineCountLimit` too low; another instance holding the quota |
| Few cases | Sampling cap; narrow `include` whitelist |
| No upload at all | `appId` doesn't match the policy `selector`; `SP_API_URL` unreachable; agent offline |
## Keep record and replay environments separate
| Environment | Agent recording | Role |
|-------------|-----------------|------|
| Production / staging | On | Build the case corpus from real traffic |
| Test / CI replay host | Off or minimal | Avoid recording a second corpus during replay |
Use consistent `sp.mocker.tags` (e.g. `env=prod`) when filtering cases by source environment.
## Tune what gets recorded {#tune-what-gets-recorded}
When the defaults don't fit — you want to control the sampling rate, record only some APIs, or restrict recording hours — write an app-level `RecordingPolicy` (`priority > 0` overrides the global default):
```bash
sp policy recording validate -f recording.yaml --json
sp policy recording apply -f recording.yaml --json
```
Tunables: `ratePerHundredSeconds` (sampling), `timeWindow` (hours), `operations.include/exclude` (API scope), `serializeSkip`, `timeMock`. Field-by-field reference and full examples: [Policy YAML guide · RecordingPolicy](/en/testing/policy-yaml-guide#recordingpolicy).
::: warning Avoid `machineCountLimit: 1` in production
This caps how many instances may record concurrently in an env group. With `1`, the first instance can hold the slot after it goes away, leaving others in "not recording". Prefer omitting the field (unlimited) or setting it ≥ your instance count.
:::
::: info Two known boundaries
* `spec.sensitiveData` on the record path does **not** change what is stored yet; for masking at view time see [SensitivePolicy](/en/testing/policy-yaml-guide#related-configuration).
* Changing `operations` include/exclude also affects the **replay schedule's** operation scope.
:::
## Next
Your cases are in the corpus → **[Replay & diff](/en/testing/replay-and-diff)**: turn them into a regression run.
---
---
url: /en/testing/replay-and-diff.md
---
# Replay and diff
Replay turns the cases you collected in [Record](/en/testing/recording) into a **regression run**: the original entry requests are sent to your **test instance** as-is, dependency calls (database, external HTTP, …) are automatically mocked from recorded data, and when the run finishes each case gets an automatic pass/fail from comparing recorded vs replayed responses.
Continuing the `order-service` example: production traffic has built a corpus of cases, and now you want to verify a new build in the test environment for regressions.
## Step 1 · Prepare the test instance
Run the **new build** in the test environment, same agent, same `appId`:
```bash
java -javaagent:sp-agent.jar \
-Dsp.app.id= \
-Dsp.api.url=http://:8090 \
-jar order-service-new.jar
```
Note its base URL, e.g. `http://order-service.test:8080` — this is **`targetEnv`**, the destination for replayed traffic.
::: warning Record in prod, replay in test
Replay sends **real HTTP requests** to `targetEnv` (only downstream dependencies are mocked), so the replay target should be a non-production instance unless you explicitly accept the risk. Also turn recording off (or near zero) on the replay host so the run doesn't capture a second corpus on top of the replay.
:::
## Step 2 · Start the replay
```bash
sp replay run --app --env http://order-service.test:8080 --json
```
The command returns a `planId`. Watch it to completion:
```bash
sp replay status --watch
```
::: tip Don't mix up the two URLs
`--env` (`targetEnv`) is the address of the **service under test**; `SP_API_URL` is the address of the **sp-backend service**. Confusing them is the most common integration mistake — see [CLI concepts](/en/testing/agents/concepts#replay-target-url-targetenv).
:::
What happens during the run: the schedule service preloads the cases' mocks into Redis, then re-sends each recorded entry request to `targetEnv`; your service executes its real business code, but on every dependency call the agent returns the **recorded** response — no real database or external system is touched; replay-side traffic is stored and automatically compared against the recorded side.
sp-backend logs **`Replay send start`** before each dispatched entry call and **`Replay send done`** / **`Replay send failed`** after — the entry/exit boundary for replay HTTP dispatch. See [Replay send log markers](/en/testing/reference/replay-send-log-markers).
## Step 3 · Read the results
A case **passes** when compare finds no material differences. **Failed** cases show a diff scene:
* **Value diff** — the dependency was called, but the response body differs
* **Missing call** — a dependency called during record was not called during replay
* **Main response diff** — the entry response differs from the recording
Quick triage from the command line:
```bash
sp replay case list --plan --json # which cases failed
sp diagnose replay --failed-only --out-dir .sp-work --json # failure detail + diff artifacts on disk
```
Once you have a difference's `diffId`, inspect the full single diff: `sp replay diff get --out-dir .sp-work --json`.
## Failures? Don't call them bugs yet
**Most failures are not bugs.** Timestamps, random IDs, pod IPs, and session tokens change on every run — they will always "differ" without anything being wrong. The last two workflow steps exist for exactly this:
* **[Review diffs](/en/testing/review-diffs-in-the-web-ui)** — read each diff in the workbench, accept the ones that aren't real bugs, and let true failures stand out
* **[Configure compare rules](/en/testing/compare-rules-web-ui)** — turn always-changing fields into rules so future replays stop false-alarming
Rules can also be declared in YAML (`sp policy compare`) for CI and GitOps — see [Policy YAML guide · CompareRulePolicy](/en/testing/policy-yaml-guide#comparerulepolicy).
## Terminology
| Concept | Meaning |
|---------|---------|
| `targetEnv` / `--env` | Base URL of the service receiving replayed entry traffic |
| `planId` | Container for the whole run |
| `planItemId` | One operation (API path) within the plan |
| `replayId` | One replay execution of a single case |
| Case | One recorded entry request + its dependency mockers |
## Replay scope
Which cases replay is determined by the plan request's time range and operation filters, plus the recording policy's `operations` include/exclude. To expand coverage, go back to [Record](/en/testing/recording) and record more traffic.
## Automation
Humans review diffs in the workbench; CI and AI agents should use `sp diagnose replay --json` and the `--out-dir` artifacts from the [output contract](/en/testing/agents/output-contract). For deploy-triggered replays and pipeline gates, see [Webhook and CI/CD](/en/testing/webhook-and-ci).
## Next
The run finished with failing cases → **[Review diffs](/en/testing/review-diffs-in-the-web-ui)**: understand them and clear the noise.
---
---
url: /en/testing/review-diffs-in-the-web-ui.md
---
# Review replay diffs
After a replay run from [Replay](/en/testing/replay-and-diff), the workbench shows which cases failed and why. This page covers the human review workflow in the **Web console** — reading a diff, and accepting the differences that aren't real bugs. Prefer the command line? `sp replay case list --plan ` and `sp diagnose replay ` yield the same failure data (see [Replay and diff](/en/testing/replay-and-diff)).
Most differences aren't bugs. Timestamps, random tokens, and IDs change on every call — they'll always "differ" without anything being wrong. Your job is to accept those, so the real failures stand out.
## Accept a difference: one-off, or permanent
There are two ways to make a difference stop counting, and picking the right one is the whole game:
* **Mark it passed on this case** — *"In this run, this difference is fine."* Applies now, only to this run. Use it after you've reviewed a case and decided it isn't a real bug.
* **Write a compare rule** — *"From now on, never compare this field."* Permanent, applies to every future replay. Use it for fields that are different by nature (timestamps, random IDs) that should never be compared.
One line to choose: **just changing this run's result → mark it passed; want it to stick forever → write a rule.**
| | Mark passed | Compare rule |
|---|---|---|
| Scope | Just this run | Every replay, from now on |
| Lifespan | One-off — gone on the next replay | Permanent — saved in your policy |
| Use it for | A case you've reviewed and accepted | A field that always changes |
::: tip A rule only applies on the next replay
A compare rule doesn't touch a run that already finished. To apply a new rule to the run you're looking at without replaying, use [Recompare](#recompare-apply-rules-to-this-run).
:::
## Open a case and read its differences
1. Open a **replay run** and pick a **failed case** — the left panel lists them with red dots.
2. **Click a span** in the call tree on the right.
The diff opens: the recorded response on the left, the replayed response on the right. Fields that differ are highlighted; the header shows how many differences the case has.

## Ignore a field
**This is the one you'll reach for most.** A field like a timestamp differs on every replay and you never want to compare it.
1. In the diff, **hover the line** that differs. An **eye-off icon** appears at the left edge — click it. (Or right-click the line, or use the **Ignore rules** button at the top.)
2. Choose **Ignore this field's differences**.
3. Pick **This case only**.
The field is struck through, the difference is gone, and the case's pass rate updates.

### Ignore it more widely
**This case only** fixes just the case in front of you. But a timestamp differs on *every* case, so ignoring it one case at a time is tedious. To stop comparing a field more widely, pick a bigger scope in the same menu:
| Pick | Ignores the field for | Takes effect |
|---|---|---|
| **This case only** | Just this one case | Now |
| **This endpoint only** | Every case of this endpoint | Next replay |
| **All endpoints** | The whole application | Next replay |
The two wider scopes write a **compare rule**, which takes effect on the *next* replay — so the run you're looking at doesn't change yet. Softprobe shows a **Recompare** button to apply it now; see [Recompare](#recompare-apply-rules-to-this-run).
::: tip Ignore every field with the same name
The menu also offers **Ignore all "…" fields** — handy when the same field name (say `updatedAt`) appears in several places and you want them all gone at once.
:::
## Mark a case passed
Sometimes a difference is real but you're OK with it — an intentional change, or a Softprobe artifact. Instead of ignoring fields one by one, accept the **whole case**.
1. On the failed case, click **Mark as passed**.
2. Pick a reason: **Difference is expected (by design)**, or **Softprobe collection/comparison issue**.
3. Optionally add a note, then confirm.
The case moves to passed and the pass rate updates. Your reason and note are kept for later review.

## Recompare: apply rules to this run {#recompare-apply-rules-to-this-run}
A rule you just wrote only kicks in on the **next** replay — it doesn't change a run that already finished. **Recompare** applies your latest rules to the run you're looking at now, re-checking its stored responses. It does **not** replay any traffic.
When you add or remove a rule from the diff, a **Recompare** button appears in the run header. Click it. Softprobe re-checks the run and updates the case list, counts, and pass rate together.

::: warning Recompare is not replay
Replay re-sends traffic to your service. Recompare only re-judges responses that are already stored, against your current rules — nothing hits your service.
:::
## Un-ignore a field
Changed your mind? **Right-click the struck-through line** and choose **Un-ignore (restore comparison)**. The field goes back to being compared.
You don't need to remember how you ignored it — Softprobe removes whatever is hiding the difference (the case mark, or the rule) and tells you what it removed. If the rule lives in your global defaults, it points you to the compare-rules page instead of leaving you stuck.
## See what's already ignored
To see everything ignored on a case without opening each span, click the **"N ignored"** chip at the top of the case. A panel lists every ignored field, grouped by call, with the rule that caught it. Hover any row to un-ignore it right there.

## Next
Ignoring the same field on every single case? Time to turn it into a rule → **[Configure compare rules](/en/testing/compare-rules-web-ui)**: configure once, and every future replay stops false-alarming.
---
---
url: /en/testing/compare-rules-web-ui.md
---
# Configure compare rules
Compare rules decide which replay differences **don't count**. The principle: *what you configure is skipped; everything else is compared strictly.* Accepting diffs one by one in [Review diffs](/en/testing/review-diffs-in-the-web-ui) only covers one run; turning always-changing fields (timestamps, random IDs) into rules stops the false alarms on every future replay — this step closes the loop.
This page covers setting rules in the **Web console's visual editor** — pick a rule type, fill in a field or two, click add. The visual editor is the friendlier path for day-to-day use; the exact same rules can be written declaratively in the [Policy YAML guide](/en/testing/policy-yaml-guide) for GitOps and CI.
## Where to configure rules
There are two places, for two scopes:
| | Global default rules | Application rules |
|---|---|---|
| Open from | Settings → Compare Rules | Workbench → Configuration → Compare Rules |
| Applies to | Every application | The current application only |
| Rule types | All types, in six tabs | The three common types plus per-endpoint overlays; the rest via YAML |
An application panel links to the global defaults at the top, so you always see what else is in effect.

The global page groups the rule types into six tabs — one per type below.

## Ignore a field by path
**The most common rule — stop comparing a field.** Ideal for volatile fields: timestamps, trace IDs, random tokens. The field, and everything under it, is removed from the comparison.
**How to add it:** open the **Ignore by path** tab, type the field path in **Ignore fields**, and click **Add**.

**What to type:** a field path. Both `data.traceId` (dot form) and `/data/traceId` (JSON Pointer) work. Use `*` for one level and `**` for any depth, e.g. `/data/*/updatedAt`.
::: tip The whitelist input (rarely needed)
The same tab has an **Include paths (whitelist)** input at the top. Add anything here and **only** those paths are compared, everything else ignored — the opposite of the ignore list. Leave it empty (the normal case) to compare everything.
:::
## Ignore an entire dependency type
**A coarse switch — drop all differences from one kind of downstream call.** For example, ignore every Redis difference, or every difference from one database query.
**How to add it:** open the **Dependency types** tab. Enter the **type** (e.g. `Redis`, `Database`, `Dubbo`, `HttpClient`) and, optionally, a specific dependency **name**. Leave the name empty to ignore the whole type.

In a run, a call ignored this way shows an **"Entire category ignored"** chip instead of per-field strikethroughs — the whole call is dropped at once.
## Ignore by a condition (CEL)
**The most flexible rule — ignore a difference when a condition is true.** Use it when matching by path or field name isn't enough, e.g. "ignore any field whose recorded and replayed values are both timestamps." After the comparison runs, each difference is tested against your condition; if it matches, the difference is dropped.
**How to add it:** open the **Ignore by condition (CEL)** tab, click **Add rule**, optionally name it, and write the condition. A template picker offers ready-made conditions.

Variables you can use: `left` / `right` (recorded / replayed value), `path` / `pointer` (field path), `fieldName`, `category`, `time_tolerance_ms`. Helpers: `isUUID`, `isIP`, `isTimestamp`, `toTimestamp`, `toNumber`.
Examples:
* Both values are timestamps: `isTimestamp(left) && isTimestamp(right)`
* A generated ID: `fieldName == "requestId" && isUUID(right)`
## Normalize a value before comparing
**Round or reshape a value so noise doesn't register** — e.g. round a float so precision differences don't count.
**How to add it:** open the **Value transform** tab, enter the field **path** and a CEL **expression** on the value (`value` is the field's original value), then click **Add transform**.

Example — round to two decimals: path `/data/orders/*/total`, expression `math.round(value * 100) / 100`.
## Decode an encoded field before comparing
**Decode base64/gzip JSON so the comparison sees real data**, instead of reporting "these two encoded strings differ."
**How to add it:** open the **Decompress** tab, enter the field **path**, and pick the **codec** (`Base64 + JSON`, `Gzip + Base64 + JSON`, or `Plain JSON`).

## Match array elements when order varies
**Compare an unordered array as a set, not by position.** By default arrays are compared by index — when element order changes between record and replay, that produces false "missing / new element" differences.
**How to add it:** open the **Array matching** tab, enter the array **path**, pick a **strategy**, and (for `By key`) enter the **key field(s)**. Click **Add array config**.

| Strategy | When to use |
|---|---|
| By index | Default — compare position by position. |
| By key | Pair elements by a key field (enter the key, e.g. `orderId`). |
| By LCS | Longest common subsequence — best-effort alignment without a key. |
## Apply rules to specific endpoints only
The rules above apply to every endpoint. To add rules for just some endpoints, use the **Per-endpoint** section in an application panel. Click **Add endpoint rules**, enter the endpoints to match — exact names and/or glob patterns like `/api/order/*` — then fill in that group's own rule table.
## Where quick rules from the diff go
When you [ignore a field in a run](/en/testing/review-diffs-in-the-web-ui#ignore-a-field), it writes into one of these rule types:
| Diff action | Becomes |
|---|---|
| Ignore this field's differences | An ignore-by-path rule |
| Ignore all "…" fields | A CEL rule matching that field name |
| Set as array key | An array rule (By key) |
Scoped to an endpoint, it lands in a per-endpoint group; scoped to the whole app, at the top level.
## Next
With rules in place, go back to [Replay](/en/testing/replay-and-diff) and run again (or use **Recompare** in [Review diffs](/en/testing/review-diffs-in-the-web-ui) to verify immediately) — whatever remains in the failure list is now worth looking at.
To automate the whole workflow — deploy-triggered replays and pipeline gates — see [Webhook and CI/CD](/en/testing/webhook-and-ci); for rules in GitOps, see the [Policy YAML guide](/en/testing/policy-yaml-guide).
---
---
url: /en/testing/policies.md
---
# Policies overview
Softprobe Testing uses **declarative YAML policies** (`apiVersion: softprobe.ai/v1`), merged by `metadata.priority` and applied by sp-backend at runtime.
::: tip Policies are optional tuning, not a prerequisite
Built-in global defaults (priority 0) make recording and replay work out of the box. You only write your own policy when you want to **change** the default behavior — control sampling, narrow operation scope, ignore noisy fields (`priority > 0` overrides). Get the core workflow running first, then come back to tighten.
:::
Separate **how to run each phase** from **policy configuration**:
| Phase | Operations | Policy config |
|-------|------------|---------------|
| **1 · Record** | [Record traffic](/en/testing/recording) | [RecordingPolicy](#recording-policy) below |
| **2 · Replay** | [Replay and diff](/en/testing/replay-and-diff) | [MockPolicy](#mock-policy), [CompareRulePolicy](#compare-policy) below |
Field reference and full examples: [Policy YAML guide](/en/testing/policy-yaml-guide) · [sp policy command](/en/testing/commands/policy)
## CLI quick reference
```bash
sp policy recording validate -f recording.yaml --json
sp policy recording apply -f recording.yaml --json
sp policy mock apply -f mock.yaml --json
sp policy compare apply -f compare.yaml --json
```
Higher **`metadata.priority`** wins on conflicts. Built-in priority-0 globals exist; use `priority > 0` on app policies.
## Lifecycle phases
| Phase | Kind | When to configure | CLI |
|-------|------|-------------------|-----|
| **1 · Record** | `RecordingPolicy` | Before traffic | `sp policy recording` |
| **2 · Replay** | `MockPolicy` | Before `sp replay run` | `sp policy mock` |
| **2 · Replay** | `CompareRulePolicy` | Before `sp replay run` | `sp policy compare` |
Full lifecycle: [Getting started](/en/testing/getting-started)
***
## RecordingPolicy {#recording-policy}
**For the [Record](/en/testing/recording) stage · apply before traffic**
Controls **what the agent records**: sampling, time window, operation include/exclude, serialize skip, record-time time mock.
* **Sampling** — `ratePerHundredSeconds` (`0` = no record), optional `machineCountLimit` (omit = unlimited)
* **Time window** — `daysOfWeek`, `from` / `to` (agent JVM local timezone)
* **Operations** — `exclude` globs; non-empty `include` switches to whitelist mode
* **Serialize skip** — `serializeSkip` by class and field names
* **`timeMock`** — fix `java.time.*` at record time
**Operational steps:** [Record traffic](/en/testing/recording)
**YAML fields and examples:** [Policy YAML guide · RecordingPolicy](/en/testing/policy-yaml-guide#recordingpolicy)
::: info Notes
* `spec.sensitiveData` is **not** applied on the agent record path yet; use `matchTolerance` for mock-key noise and `SensitivePolicy` for view-time masking (see YAML guide).
* Changing `operations` include/exclude updates **replay schedule** operation scope without a separate schedule edit.
:::
```bash
sp policy recording validate -f my-recording.yaml --json
```
***
## MockPolicy {#mock-policy}
**For the [Replay](/en/testing/replay-and-diff) stage · apply before `sp replay run`**
Controls **whether dependencies are mocked** at replay, mock-key tolerance, cross-app dependencies, and fallback when no mock matches.
* **`mockByDefault`** — mock all deps by default (`skipMock` exceptions), or the inverse with `forceMock`
* **`Category:operationGlob`** — e.g. `HttpClient:/payment/**` (**not** entry types like `Servlet`)
* **`matchTolerance`** — ignore volatile headers, query params, body paths
* **`multiServiceDependencies`** — mock downstream apps from the same session
* **`fallback`** — `FAIL` (default), `PASS_THROUGH`, `RETURN_DEFAULT`
Global defaults **force-mock** `DynamicClass:SystemTime.**` and `RandomSource.**`; user `skipMock` has no effect.
**YAML fields and examples:** [Policy YAML guide · MockPolicy](/en/testing/policy-yaml-guide#mockpolicy)
[Full dependency category list](/en/testing/policy-yaml-guide#dependency-categories)
***
## CompareRulePolicy {#compare-policy}
**For the [Replay](/en/testing/replay-and-diff) stage · apply before `sp replay run`**
Controls **diff noise** during replay comparison (not mock behavior).
* **`excludePaths` / `includePaths`** — JSON Pointer (with globs)
* **`defaults.timeToleranceMs`** and CEL **`validations`** — drop diffs by rule (including per-`category` rules)
* **`operationSpecs`** — per-entry-operation overlays (**do not** put operation names on `selector`)
**YAML fields and examples:** [Policy YAML guide · CompareRulePolicy](/en/testing/policy-yaml-guide#comparerulepolicy)
Use `sp replay diff` after replay, then tighten policy rather than changing application code.
***
## Dynamic classes (not RecordingPolicy)
Register methods in **dynamic class configuration** (dashboard/API), not in `RecordingPolicy`. Control replay mocking via **MockPolicy** `UserDynamic` / `DynamicClass` rules. See [Policy YAML guide · Related configuration](/en/testing/policy-yaml-guide#related-configuration).
## Policy kinds and server modules
| Kind | CLI | Server module |
|------|-----|---------------|
| `RecordingPolicy` | `sp policy recording` | `RecordingPolicyService` |
| `MockPolicy` | `sp policy mock` | `MockPolicyService` |
| `CompareRulePolicy` | `sp policy compare` | `CompareRulesService` |
Example files ship in `sp-policy-rules/src/main/resources/examples/`.
## Agent workflow
```bash
# Always validate before apply
sp policy recording validate -f recording.yaml --json
sp policy recording apply -f recording.yaml --json
```
CI should fail on `valid: false` or a non-zero exit.
## GitOps
```bash
sp policy recording export prod-policy-id -o policies/recording-prod.yaml
git commit -m "chore: sync recording policy"
```
See [GitOps policies](/en/testing/examples/gitops-policies).
## Relationship to legacy config
`sp config legacy schedule` and Mongo `ServiceCollectConfiguration` are **not** the source of truth after the policy-rules migration. Use `sp policy recording` for operation include/exclude that affects both agent and replay scope.
## Related
* [Record traffic](/en/testing/recording)
* [Replay and diff](/en/testing/replay-and-diff)
* [Policy YAML guide](/en/testing/policy-yaml-guide)
* [CLI: policy command](/en/testing/commands/policy)
---
---
url: /en/testing/webhook-and-ci.md
---
# Webhook-triggered replay and CI/CD integration
Run record-and-replay regression automatically after a deploy or merge against a **test** instance, then gate the pipeline on compare results. This page covers two trigger styles, how to collect results, and GitHub Actions / Jenkins patterns.
::: tip Record first, then automate replay
Webhooks and CI do not create cases for you. Walk the core workflow first — [Record](/en/testing/recording) and [Replay](/en/testing/replay-and-diff) (`appId`, policies, reachable `targetEnv`) — then hand it to automation.
:::
## Should you use the `sp` CLI or REST APIs?
| Scenario | Recommendation |
|----------|----------------|
| Full pipelines (GitHub Actions, Jenkins, GitLab CI) | **`sp` CLI** (`--json`, stable exit codes, built-in polling) |
| Deploy hooks (Argo CD, K8s Job, shell) that only need “deploy OK → start replay” | **GET webhook** (one-line `curl`) |
| Custom orchestration with your own HTTP client and request bodies | **REST** (`POST /api/createPlan` + report APIs) |
**Rule of thumb:** When the pipeline must **create a plan → wait → list failures → pass/fail**, use **`sp replay run --watch`** and **`sp replay case list --failed`** per the [output contract](/en/testing/agents/output-contract). Use GET webhook to **trigger only**; fetch results in a later step with `sp` or authenticated report APIs.
See [replay command](/en/testing/commands/replay) and [authentication](/en/testing/agents/authentication).
## Architecture (brief)
```mermaid
sequenceDiagram
participant CI as CI/CD or webhook
participant API as sp-backend schedule + report
participant SUT as Service under test targetEnv
CI->>API: createPlan
API->>SUT: replay recorded entry HTTP
SUT-->>API: replay traffic and compare results
CI->>API: poll progress / query failed cases
```
* **`SP_API_URL`**: sp-backend (storage, schedule, report) — **not** the service under test.
* **`targetEnv`**: base URL of the test instance (e.g. `http://order-service.test:8080`). Do not confuse with `SP_API_URL` — [CLI concepts: targetEnv](/en/testing/agents/concepts#replay-target-url-targetenv).
## Option 1: Webhook (GET) trigger
The schedule service exposes **GET** `/api/createPlan` for deploy webhooks, alerts, or simple `curl` calls. The server sets `operator` to `Webhook` and, by default, selects cases from the last **24 hours** for the app (narrow with time query params).
### Example request
```bash
curl -sS -G "${SP_API_URL}/api/createPlan" \
-H "access-token: ${SP_TOKEN}" \
--data-urlencode "appId=YOUR_APP_ID" \
--data-urlencode "targetEnv=http://your-service.test:8080" \
--data-urlencode "planName=post-deploy-smoke" \
--data-urlencode "caseCountLimit=50" \
--data-urlencode "enableMock=true"
```
### Query parameters
| Parameter | Required | Description |
|-----------|----------|-------------|
| `appId` | yes | Registered application id |
| `targetEnv` | yes | Test instance base URL (`http://` or `https://`) |
| `caseSourceFrom` | no | Case window start (Unix **milliseconds**); default ~24h ago |
| `caseSourceTo` | no | Case window end (ms); default now |
| `caseCountLimit` | no | Max cases to replay |
| `planName` | no | Display name for the plan |
| `operationIds` | no | Repeatable; restrict to operation ids |
| `enableMock` | no | `true` / `false` |
| `caseTags` | no | JSON string for tag filters |
### Response
On success, `result` is `1` and `data` contains **`replayPlanId`** (use as `planId` when polling):
```json
{
"result": 1,
"desc": "success",
"data": {
"replayPlanId": "plan-abc123"
}
}
```
::: warning
GET webhook **only creates** a plan; it does not wait for completion. Poll progress or query the report in a follow-up CI step.
:::
::: info POST vs GET
`POST /api/createPlan` (used by `sp replay run`) accepts the full `BuildReplayPlanRequest` (time window, `operationCaseInfoList`, pressure settings, etc.). For complex case selection, use POST or the CLI instead of stretching GET params.
:::
## Option 2: CLI / REST create (recommended for CI)
Same as [Replay and diff](/en/testing/replay-and-diff):
```bash
export SP_API_URL=https://your-tenant.softprobe.ai
export SP_TOKEN="${SP_TOKEN}"
sp replay run \
--app YOUR_APP_ID \
--env http://your-service.test:8080 \
--from -24h \
--name "ci-${GITHUB_SHA:-build}" \
--watch \
--json
```
`--watch` polls `GET /api/progress?planId=…` until finished. The CLI may still exit **0** when compare failures exist — pipelines must check failed cases (below).
REST equivalent:
```http
POST /api/createPlan
Content-Type: application/json
access-token:
{
"appId": "YOUR_APP_ID",
"targetEnv": "http://your-service.test:8080",
"caseSourceFrom": 1717000000000,
"caseSourceTo": 1717086400000,
"enableMock": true,
"planName": "ci-build-42"
}
```
## Getting results
### 1. Poll progress
```bash
sp replay status plan-abc123 --watch --json
```
REST: `GET /api/progress?planId=plan-abc123` on the same `SP_API_URL`. When `percent` reaches 100, scheduling is done.
### 2. Pass / fail gate
After progress finishes:
```bash
sp replay case list --plan plan-abc123 --failed --limit 1 --json
```
Fail the job if `data.items` is non-empty. For diff artifacts:
```bash
sp replay diff get --out-dir .sp-work --json
sp diagnose replay plan-abc123 --failed-only --out-dir .sp-work --json
```
See [Example: diagnose replay failure](/en/testing/examples/agent-diagnose-replay).
### 3. Dashboard (humans)
Open the same `planId` in the workbench or dashboard for diff trees; automation should rely on CLI/`--json`.
## GitHub Actions example
Run after the **test** deployment with the agent attached. Secrets: `SP_API_URL`, `SP_TOKEN`.
```yaml
name: Softprobe replay gate
on:
workflow_dispatch:
push:
branches: [main]
jobs:
replay-regression:
runs-on: ubuntu-latest
env:
SP_API_URL: ${{ secrets.SP_API_URL }}
SP_TOKEN: ${{ secrets.SP_TOKEN }}
SP_APP_ID: ${{ vars.SP_APP_ID }}
SP_TARGET_ENV: http://my-service.test:8080
steps:
- name: Install sp
run: |
curl -fsSL -o sp "${SP_DOWNLOAD_URL:-https://install.softprobe.ai/artifacts/sp/latest}/sp-linux-amd64"
chmod +x sp && sudo mv sp /usr/local/bin/
- name: Preflight
run: |
sp health --json
sp record case list --app "$SP_APP_ID" --since -24h --json
- name: Run replay and wait
id: replay
run: |
sp replay run \
--app "$SP_APP_ID" \
--env "$SP_TARGET_ENV" \
--from -24h \
--name "gha-${GITHUB_SHA}" \
--watch \
--json | tee replay.ndjson
PLAN_ID=$(grep '"command":"replay run"' replay.ndjson | tail -1 | jq -r '.data.planId // .data.replayPlanId')
echo "plan_id=$PLAN_ID" >> "$GITHUB_OUTPUT"
- name: Fail if any case failed
run: |
COUNT=$(sp replay case list --plan "${{ steps.replay.outputs.plan_id }}" --failed --json \
| jq '[.data.items[]?] | length')
if [ "$COUNT" -gt 0 ]; then
echo "Replay had $COUNT failed case(s)"
exit 1
fi
```
Optional fire-and-forget webhook after deploy:
```yaml
- name: Trigger replay webhook
run: |
curl -fsS -G "$SP_API_URL/api/createPlan" \
-H "access-token: $SP_TOKEN" \
--data-urlencode "appId=$SP_APP_ID" \
--data-urlencode "targetEnv=$SP_TARGET_ENV" \
--data-urlencode "planName=deploy-${{ github.run_id }}"
```
Policy validation on PRs: [CI policy gate example](/en/testing/examples/ci-policy-gate).
## Jenkins example
Bind `SP_TOKEN` as credentials:
```groovy
pipeline {
agent any
environment {
SP_API_URL = credentials('sp-api-url')
SP_TOKEN = credentials('sp-token')
SP_APP_ID = 'YOUR_APP_ID'
SP_TARGET_ENV = 'http://my-service.test:8080'
}
stages {
stage('Replay') {
steps {
sh '''
sp replay run \
--app "$SP_APP_ID" \
--env "$SP_TARGET_ENV" \
--from -24h \
--name "jenkins-${BUILD_NUMBER}" \
--watch \
--json > replay.ndjson
PLAN_ID=$(grep '"command":"replay run"' replay.ndjson | tail -1 | jq -r '.data.planId // .data.replayPlanId')
FAILED=$(sp replay case list --plan "$PLAN_ID" --failed --json | jq '[.data.items[]?] | length')
test "$FAILED" -eq 0
'''
}
}
}
}
```
Use `curl -G …/api/createPlan` in a post-deploy step; wait and gate in a later stage with `sp replay status --watch`.
## Security and operations
* Store **`SP_TOKEN` in CI secrets** only — [authentication](/en/testing/agents/authentication).
* If webhook URLs are reachable from the internet, restrict access and always send **`access-token`**.
* Replay sends **real HTTP** to `targetEnv`; point webhooks at test/staging instances — [Replay and diff](/en/testing/replay-and-diff).
* Lower or disable recording on replay hosts during runs.
## Related
* [Replay and diff](/en/testing/replay-and-diff)
* [Getting started](/en/testing/getting-started)
* [CLI: replay](/en/testing/commands/replay)
* [API mapping](/en/testing/reference/api-mapping)
* [CLI quickstart](/en/testing/getting-started)
---
---
url: /en/testing/agents/overview.md
---
# For AI agents
This page is the primary entry point for authors of **OpenCode / spcode**, **Claude Code**, **Codex**, **Cursor**, and other agent hosts that invoke SoftProbe through shell tools.
## Feed these docs to your agent
The whole documentation set is published in [llmstxt.org](https://llmstxt.org/) format so an agent can ingest it without scraping HTML:
| URL | Contents |
|-----|----------|
| [`/llms.txt`](/llms.txt) | Curated index of every page with descriptions — a small entry point |
| [`/llms-full.txt`](/llms-full.txt) | The entire docs corpus in one plain-text file |
| `‹any page›.md` | The Markdown source of a single page (append `.md` to its URL) |
Point your agent at `/llms.txt` first; it will follow links into the full text or per-page `.md` as needed.
## Design goals
1. **One process, one job** — each tool call runs a single `sp` command with explicit flags. Do not rely on shell aliases or interactive prompts.
2. **Always pass `--json`** for machine parsing unless you are showing output to a human in chat.
3. **Use artifacts for large payloads** — diff bodies, log downloads, and record queries write files under `--out-dir`; stdout returns paths and summaries only.
4. **Backend data only** — use API commands (`sp app`, `sp replay`, `sp record`, …) for cluster state.
## Recommended tool shape
Wrap `sp` as a shell tool with a fixed argv prefix:
```bash
sp --json --profile "${SP_PROFILE:-default}" ...
```
Environment variables agents should set in the host config:
| Variable | Purpose |
|----------|---------|
| `SP_API_URL` | Backend base URL (e.g. `http://127.0.0.1:8090`) |
| `SP_TOKEN` | JWT from `sp auth login` or CI secret |
| `SP_PROFILE` | Named profile in `${XDG_CONFIG_HOME}/softprobe/config.jsonc` or `sp.jsonc` |
| `SP_CONFIG` | Extra config file path loaded after `sp.jsonc` and before `--config` |
`sp` also reads `${XDG_CONFIG_HOME:-~/.config}/softprobe/config.jsonc` for
shared SoftProbe settings and `${XDG_CONFIG_HOME:-~/.config}/softprobe/sp.jsonc`
for CLI-specific overrides. Agents should prefer `SP_API_URL` and `SP_TOKEN`
when running in ephemeral CI containers.
## Lifecycle command order
Use these job-oriented commands before low-level building blocks:
1. `sp doctor --json`
2. `sp app create --json` → save `data.appId`
3. `sp policy recording apply -f … --json`
4. Download `sp-agent.jar` from `install.softprobe.ai`, then run `sp agent command --app --agent-jar ./sp-agent.jar --json`
5. Start the app with `data.startCommand`; send traffic
6. `sp record case list --app --since -1h --json`
7. `sp policy mock apply` / `sp policy compare apply`
8. `sp replay run --app --env --json`
9. `sp replay status --json` or `sp diagnose replay --json`
## Typical skill flows
### Diagnose a failed replay
```mermaid
sequenceDiagram
participant Agent
participant SP as sp CLI
participant API as sp-backend
Agent->>SP: app list --json
SP->>API: GET /api/applications/list
Agent->>SP: replay case list --plan X --failed --json
SP->>API: report/storage APIs
Agent->>SP: replay diff get diffId --out-dir .sp-work --json
SP->>API: GET /api/report/queryDiffMsgById/{id}
Agent->>Agent: Read artifact file locally
```
Commands: see [Diagnose replay failure](/en/testing/examples/agent-diagnose-replay).
### Change policy and re-run
1. `sp policy recording validate -f policy.yaml --json`
2. `sp policy recording apply -f policy.yaml --json`
3. `sp replay run --app … --env --json`
4. `sp replay status --watch --json`
## Migrating from `sp_api` (spcode plugin)
The OpenCode `@spcode/plugin` tool `sp_api` uses named endpoints (`diff_detail`, `query_replay_case`, …). The `sp` CLI exposes the same operations as **stable subcommands**.
| Old | New |
|-----|-----|
| `sp_api endpoint=list_applications` | `sp app list --json` |
| `sp_api endpoint=diff_detail diffId=…` | `sp replay diff get … --out-dir … --json` |
| `sp_api endpoint=find_traces_by_attr …` | `sp trace find … --json` |
Skills should be updated to call `sp` so behavior is consistent outside OpenCode.
## Error handling for agents
| Exit code | Meaning | Agent action |
|-----------|---------|--------------|
| `0` | Success | Parse stdout JSON |
| `1` | API or business error | Read stderr JSON `error` field; may retry or ask user |
| `2` | Usage / missing config / auth | Fix argv or run `sp auth login` / set `SP_TOKEN` |
| `3` | Auth required | Refresh token |
See [Output contract](./output-contract) and [Exit codes](/en/testing/reference/exit-codes).
## What not to call
* Agent write APIs (`POST /api/storage/record/save`, batch saves) — instrumentation only
* OTLP ingestion (`/v1/traces`) — use collectors
* Mutating `sp ops` without `--confirm` — blocked by design
## Related pages
* [Output contract](./output-contract)
* [Versioning](./versioning)
* [Commands](/en/testing/commands/)
---
---
url: /en/testing/agents/introduction.md
---
# Introduction
SoftProbe is a record-and-replay testing system for Java services. It attaches to an application as a `-javaagent`, observes real traffic in the background, records request and dependency data, then replays recorded cases later with automatic mocking and automatic comparison.
The SoftProbe CLI (`sp`) is the command-line interface to the SoftProbe backend (**sp-backend**). It is specified **AI-agent first**: stable JSON output, predictable exit codes, and artifact files for large diagnosis payloads.
## How SoftProbe Works
SoftProbe has four moving parts:
| Part | Role |
|------|------|
| **Instrumented application** | Your Java service, started with `-javaagent:/path/to/sp-agent.jar`. |
| **SoftProbe Java agent** | Weaves bytecode at runtime, similar operationally to an OpenTelemetry Java agent. It records HTTP, DB, cache, RPC, config, auth, dynamic class, and other dependency data without application code changes. |
| **SoftProbe backend** | Stores recorded cases, policies, replay plans, replay logs, diff results, extraction rules, app config, and agent heartbeats. |
| **`sp` CLI** | Registers apps, configures policies/rules, checks agent status, queries recorded data, starts replay plans, and diagnoses replay failures. |
The normal lifecycle is:
1. **Setup** the CLI and confirm the backend is reachable.
2. **Create an app** and get an `appId`.
3. **List the app** and confirm it is visible to the current user or agent token.
4. **Set recording policy** so the agent knows what traffic to capture.
5. **Install the Java agent** (`sp-agent.jar`) on the service host or image.
6. **Run the app with the agent in recording mode**, pinning `sp.app.id` to the registered `appId`, then send real or test traffic through the app.
7. **List recorded cases** and verify the expected requests were captured.
8. **Set replay policy**: mock policy and compare policy decide what is mocked and how differences are judged.
9. **Create a replay plan** from the recorded cases and run replay.
10. **Check replay results** with case, diff, log, trace, and mock-tree commands.
Replay is therefore not the first action in a fresh system. A replay plan requires recorded cases from an app that was actually run with the SoftProbe agent.
## Agent Startup Example
The agent is attached with a JVM `-javaagent` flag. Pin the app id explicitly in production; do not rely on inferred service names.
```bash
java \
-javaagent:/opt/softprobe/sp-agent.jar \
-Dsp.app.id=a1b2c3d4e5f67890 \
-Dsp.api.url=http://127.0.0.1:8090 \
-jar order-service.jar
```
`sp.app.id` is the identity SoftProbe uses to isolate recordings, pull config, and match replay data. It is separate from `OTEL_SERVICE_NAME`, which only describes telemetry resource attributes.
## Single public surface
All documented `sp` commands call **sp-backend** over HTTP (`:8090` by default). The Java agent also talks to backend services to pull config, send heartbeats, and upload recording/replay observability data.
## Who uses `sp`?
1. **AI coding agents** — replay failure diagnosis, trace lookup by business ID, policy validation, automated re-runs
2. **CI/CD** — policy gates on pull requests, scheduled replay smoke tests
3. **Platform engineers** — same commands as agents, often without `--json`
The web UI remains available for visual inspection and manual review; the CLI covers **scriptable** and **agent** workflows focused on record-and-replay.
## Relationship to other tools
| Tool | Role |
|------|------|
| **Web UI** | Visual diff, trace inspection, and console review |
| **Java agent** | In-process instrumentation, recording, replay-time mocking, and replay observability |
| **`spcode` CLI** | Local autonomous AI engine and interactive terminal workspace. Shared SSO configuration. |
| **[Policy YAML guide](/en/testing/policy-yaml-guide)** | Schema and examples for `sp policy` inputs |
## Documentation map
* [Softprobe Testing](/en/testing/) — product overview (Java agent, record/replay, policies)
* [For AI agents](./overview) — start here if you write skills or tools
* [spcode CLI](/en/testing/installation/code) — local autonomous AI engine and interactive terminal workspace
* [Commands](/en/testing/commands/) — platform, investigation, and administration
---
---
url: /en/testing/agents/concepts.md
---
# Concepts
For the Java record-and-replay product (agent, policies, replay semantics), see [Softprobe Testing](/en/testing/).
For Istio/Envoy mesh capture and SESSIFY session context (separate from the Java agent), see [Platform core concepts](/en/platform/advanced-guides/concepts).
## Application (`appId`)
A registered service under test. Recording, replay, policies, and extraction rules are all scoped by **`appId`**.
| Field | Role |
|-------|------|
| `appName` | Unique label supplied at registration (`sp app create `). |
| `appId` | System-generated id (16-character hex). Configure the Java agent and CLI with this value. |
After registration, save `data.appId` from the create response. Attach the SoftProbe Java agent to your JVM with that id and your sp-backend URL, then confirm connectivity with `sp app status ` or `sp app list --json`.
**Agent status** (`online`, `offline`, `never`) is derived from instance heartbeats, not from the app document alone. The server marks an app `offline` when the freshest heartbeat is older than the configured threshold (default 60 seconds).
**CLI reference:** [sp app](/en/testing/commands/app)
## Java agent
The SoftProbe Java agent is attached to the service under test with `-javaagent:/path/to/sp-agent.jar`. It operates like an observability agent operationally, but its purpose is test data capture and replay:
* During **recording**, it observes real requests and dependency interactions and uploads mocker data keyed by `appId` and trace/case ids.
* During **replay**, it restores recorded dependency behavior according to mock policy and emits replay data for comparison.
* It reports heartbeat/status so `sp app status ` can tell whether an app is `online`, `offline`, or `never`.
Minimum startup flags:
```bash
java \
-javaagent:/opt/softprobe/sp-agent.jar \
-Dsp.app.id= \
-Dsp.api.url=http://:8090 \
-jar app.jar
```
Pin `sp.app.id` for every production-like deployment. If the id changes between recording and replay, SoftProbe cannot reliably find the original cases or mock data.
## Replay target URL (`targetEnv`)
Replay does not use a symbolic environment label such as `staging` or `prod`. The schedule service field **`targetEnv`** is the **base URL of the running service** that will receive replayed HTTP traffic.
When you run `sp replay run --env `, the CLI sends that value as `targetEnv` on `POST /api/createPlan`. The schedule module parses it as a URI (`DefaultDeploymentEnvironmentProviderImpl`) and builds a `ServiceInstance` whose `url` is that string. Replay senders then issue requests to that URL (for example `DefaultHttpReplaySender` uses `instanceRunner.getUrl()`).
Requirements:
* Use a reachable base URL for the app under test, including scheme and host (and port when not default), for example `http://travel-ota:8080` or `https://order-service.internal:8443`.
* The URL must parse as a URI with a non-empty host; otherwise plan validation fails with *requested target env unable load active instance*.
* This is independent of **`SP_API_URL`** / `api_url` in CLI config, which points at sp-backend (storage, report, schedule APIs), not at the service being replayed.
Optional **`sourceEnv`** on the same request is a separate URI used only when you need a non-default source deployment; the demo stack often leaves it as `pro`.
**CLI reference:** [sp replay](/en/testing/commands/replay) (`--env` → `targetEnv`)
## Recording policy
Declarative YAML (`kind: RecordingPolicy`) controlling what the agent records: sampling, operation include/exclude, time windows, sensitive-field scrubbing.
* Managed via `sp policy recording`
* Schema: [Policy YAML guide](/en/testing/policy-yaml-guide)
## Mock policy
Declarative YAML (`kind: MockPolicy`) controlling replay-time mocking: skip/force mock, tolerance, dependencies.
* `sp policy mock`
## Compare rules
Declarative YAML (`kind: CompareRulePolicy`) controlling diff behavior during replay comparison.
* `sp policy compare`
Policies merge by `metadata.priority`; global defaults ship in `sp-policy-rules` JAR resources.
## Replay plan
A batch replay job with a `planId`. Created by `sp replay run`, tracked with `sp replay status`. Replay plans consume cases already recorded by an instrumented app; a fresh app with no recorded traffic has nothing meaningful to replay.
Schedule service endpoints: `/api/createPlan`, `/api/progress`, `/api/stopPlan`.
## Trace, replay, and plan ids {#trace-replay-and-plan-ids}
Platform ids tie together recording, replay, diff, and **correlated log search**. For a full reference — what each id means, where to find it, and how to triage unified logs — see **[Log correlation IDs](/en/testing/reference/log-correlation-ids)**.
| ID | Meaning | Log lookup (v1) |
|----|---------|-----------------|
| `traceId` | W3C trace id for a recorded or replayed request flow | **`sp logs --trace-id …`** or `GET /api/recorder/logs?trace_id=…` — **only v1 key** |
| `replayId` | One replay **attempt** of a case | Diff/diagnose only — copy **`traceId`** from the same case row for logs |
| `planId` | Replay plan container from `sp replay run` | Case list / diagnose — per-case **`traceId`** for logs |
| `planItemId` | Operation-level item within a plan | Same — not a log lookup key |
| `diffId` | Comparison result row for deep diff fetch | Use `sp replay diff get` — not a log lookup key |
**Where ids appear in CLI output**
| Command | Fields |
|---------|--------|
| `sp replay run --json` | `planId` |
| `sp replay case list --plan … --json` | `replayId`, `traceId`, plan item ids |
| `sp replay metadata --json` | `traceId`, linked recording metadata |
| `sp trace find … --json` | `traceId` when resolving business attributes |
| `sp diagnose replay --json` | Failed cases with ids for follow-up |
Agents should obtain `traceId` via `sp trace find` when users supply business attributes (orderId, caseId) instead of trace ids. For log diagnosis after a replay failure, use **`traceId`** from the failed replay case or the e2e **Softprobe correlation** block (`trace_id` field) — not `replayId` as a log query key.
## Historical coupling: schedule ↔ recording
Replay operation include/exclude lists on schedule configuration are **populated from recording policy at read time**, not stored independently on the schedule document.
Implications:
* Changing recording policy can change which operations appear in replay scope without editing schedule config.
* Diagnosis skills must not assume schedule Mongo documents are the sole source of operation filters.
See server comments on `ScheduleConfigurableHandler` in sp-tr-api.
Test cases are created only via **recording** (instrumented app traffic). Manual case authoring is not part of the CLI workflow.
## Related
* [For agents](./overview)
* [Commands](/en/testing/commands/)
---
---
url: /en/testing/agents/authentication.md
---
# Authentication
Console APIs require the HTTP header:
```http
access-token:
```
(Defined as `Constants.ACCESS_TOKEN` in sp-tr-api.)
## Non-interactive login (agents + CI)
### Email verification flow
1. Request code (human step or separate automation):
```http
GET /api/login/getVerificationCode/{userName}
```
2. Exchange code for token:
```bash
sp auth login --email user@corp.com --code 123456 --json
```
**REST:** `POST /api/login/verify` with body `{ "userName", "verifyCode", ... }`
3. CLI writes token to the shared config file in
`${XDG_CONFIG_HOME:-~/.config}/softprobe/config.jsonc` unless `--no-save`.
### CI / agent hosts
**CLI** (record, replay, app management) uses your user JWT:
```bash
export SP_TOKEN="eyJ..."
sp app list --json
```
**Java agent** on Softprobe Cloud uses a **tenant API key** (long-lived, scoped to the org):
```bash
export SP_TENANT_API_KEY="…" # from sp tenant key ensure or dashboard Settings
export SP_TENANT_ID="35"
sp agent command --app --json
```
Never commit tokens or API keys to git. Rotate on leak.
### Token refresh
```bash
sp auth refresh --user user@corp.com --json
```
**REST:** `GET /api/login/refresh/{userName}`
Add `--no-save` when an agent or CI job should receive a token without touching
local config:
```bash
export SP_TOKEN="$(sp auth refresh --user user@corp.com --no-save --json | jq -r .data.token)"
```
## Guest login
If enabled on the server:
```bash
sp auth login --guest --json
```
**REST:** `POST /api/login/loginAsGuest`
## OAuth
OAuth flows are browser-based. Agents should use pre-provisioned `SP_TOKEN` rather than driving OAuth interactively.
Document for humans: `GET /api/login/oauthInfo/{oauthType}`, `POST /api/login/oauthLogin`.
## Who am I
```bash
sp auth whoami --json
```
Decodes JWT `userName` or calls profile endpoint when implemented.
## Errors
| Situation | Exit code |
|-----------|-----------|
| Missing token with `--json` | `3` (`AUTH_REQUIRED`) |
| Expired or invalid token | `1` (`API_ERROR`) |
## Related
* [config](/en/testing/installation/configuration)
* [auth command](/en/testing/commands/auth)
---
---
url: /en/testing/agents/output-contract.md
---
# Output contract
All public `sp` commands follow this contract when `--json` is set.
## CLI envelope (stdout on success)
```json
{
"ok": true,
"command": "app list",
"data": { }
}
```
| Field | Type | Description |
|-------|------|-------------|
| `ok` | boolean | Always `true` on exit 0 |
| `command` | string | Normalized command name for logging |
| `data` | object | Command-specific payload (see [JSON types](/en/testing/reference/json-types)) |
## CLI envelope (stderr on failure, exit 1)
```json
{
"ok": false,
"command": "replay run",
"error": {
"code": "API_ERROR",
"message": "Human-readable summary",
"httpStatus": 500,
"backend": { }
}
}
```
`backend` optionally contains the raw SoftProbe `Response` or schedule `CommonResponse` body for debugging.
## Backend response shapes
### sp-tr-api (`Response`)
Most console APIs return:
```json
{
"responseStatusType": { "responseCode": 0, "responseDesc": "success" },
"body": { }
}
```
The CLI maps `responseCode !== 0` to exit code `1`.
### sp-replay-schedule (`CommonResponse`)
Replay control (`createPlan`, `progress`, …) uses:
```json
{
"result": 1,
"desc": "success",
"data": { }
}
```
The CLI maps `result !== 1` (per server convention) to exit code `1`.
## Artifacts (large output)
When response bodies exceed an internal threshold (recommended: 64 KiB) or when the command always produces files (diff detail, log download):
**stdout:**
```json
{
"ok": true,
"command": "replay diff get",
"data": {
"artifact": ".sp-work/diff-abc123.json",
"summary": {
"diffId": "abc123",
"diffResultCode": 1,
"categoryName": "..."
}
}
}
```
Agents should **read the artifact file** with their file tool, not expect full payloads on stdout.
Default `--out-dir`: `.sp-work/` in the current working directory (override per invocation).
## Pagination
List commands accept:
| Flag | Default | Description |
|------|---------|-------------|
| `--page` | `1` | 1-based page index |
| `--limit` | `20` | Page size (max 100 unless documented) |
| `--fields` | all | Comma-separated field filter |
Paginated JSON:
```json
{
"ok": true,
"command": "replay case list",
"data": {
"items": [],
"page": 1,
"pageSize": 20,
"total": 142,
"hasMore": true
}
}
```
## Human-readable mode (no `--json`)
* Tables on stdout
* Errors on stderr as plain text
* Colors only when stdout is a TTY and `--pretty` is set (optional; not required for v1 implementers)
## Non-interactive rule
If `--json` is set and authentication is missing, the CLI must **not** prompt. Exit `3` with:
```json
{
"ok": false,
"error": { "code": "AUTH_REQUIRED", "message": "Set SP_TOKEN or run sp auth login" }
}
```
---
---
url: /en/testing/agents/versioning.md
---
# Versioning and stability
## Binary version
`sp version` prints the CLI build version (semver). Agents may log this on every session for support correlation.
## Command argv stability
| Change type | Policy |
|-------------|--------|
| New subcommand | Allowed anytime |
| New optional flag | Allowed anytime |
| Renaming subcommand or flag | Deprecate for one minor release; print stderr warning |
| Removing subcommand or flag | Major version bump only |
Document deprecations in release notes and in command `--help` text.
## JSON field stability
| Change type | Policy |
|-------------|--------|
| New fields in `data` | Allowed; agents must ignore unknown fields |
| Renaming or removing fields | Major version bump, or add `dataVersion` in envelope |
| Changing field types | Major version bump |
Recommended: include `"schemaVersion": 1` in the CLI envelope in a future release.
## API backend compatibility
The CLI targets **sp-backend** unified deployment. Backend URL is configurable; the CLI does not pin to separate storage/schedule hostnames (those are internal to sp-backend).
When backend APIs change, update [API mapping](/en/testing/reference/api-mapping) in the same PR as the CLI implementation.
---
---
url: /en/testing/examples.md
---
# Examples
Testing examples show how to automate Softprobe with `sp`.
* Diagnose replay failures
* Look up traces and correlated logs
* Run CI policy gates
* Manage GitOps policies
---
---
url: /en/testing/commands.md
---
# Commands
Reference for public `sp` subcommands. Use `--json` on API-backed commands. See [Output contract](/en/testing/agents/output-contract).
## Lifecycle (recommended)
Job-oriented commands that follow record-and-replay order:
| Command | Synopsis |
|---------|----------|
| [setup](./setup) | Configure self-hosted backend URL; optional Spcode Service (Linux) |
| [demo](./demo) | `start`, `traffic`, `replay`, `status`, `stop` — [Travel OTA demo](https://github.com/softprobe/demo-ota) stack |
| [agent](./agent) | `download`, `command` — install jar and JVM flags |
| [record](./record) | `case list` — recorded entry cases before replay |
| [diagnose](./diagnose) | `replay`, `trace` — bundled investigation workflows |
## Platform
Connect, authenticate, manage apps and policies, run replay plans.
| Command | Synopsis |
|---------|----------|
| [config](./config) | Profiles, URL, init |
| [auth](./auth) | Login, whoami, refresh |
| [app](./app) | List, create, agent status, recent replays |
| [policy](./policy) | Recording, mock, compare YAML policies |
| [replay](./replay) | Run, status, stop, rerun plans |
| [health](./health) | Cluster health |
| `version` | CLI version string |
## Investigation
Recorded data, traces, and replay failures.
| Command | Synopsis |
|---------|----------|
| [record](./record) | Query recordings and completeness |
| [trace](./trace) | Find traces by business attributes |
| [logs](./logs) | Correlated logs by `trace_id` — see [Log correlation IDs](/en/testing/reference/log-correlation-ids) |
| [replay case](./replay-case) | List cases, metadata, mock tree |
| [replay diff](./replay-diff) | Diff artifacts, compare results |
| [extraction-rule](./extraction-rule) | Business attribute extraction rules |
Investigation commands support `--out-dir`, `--page`, and `--limit` unless noted.
## Administration
Groups, system config, diagnostics, legacy APIs.
| Command | Synopsis |
|---------|----------|
| [group](./group) | User groups and app grants |
| [grant](./group) | App grant listing (`grant list`) |
| [system](./system) | System config keys |
| [ops](./ops) | Storage and schedule diagnostics |
| [config legacy](./config-legacy) | Legacy `/api/config/*` (deprecated) |
## Global flags
```text
--json Machine-readable stdout (required for agents)
--profile Config profile name
--api-url Override backend URL
--token Override JWT (else SP_TOKEN / config)
--config Extra config file (JSONC overlay)
--quiet Suppress non-error stderr
--out-dir Directory for artifact files
--page Page number (investigation list commands)
--limit Page size / max items
```
## Cheat sheet
```bash
sp config init && sp auth login --email u@c.com --code 123456 --json
sp doctor --json
sp app create my-svc --json
curl -fsSL -o sp-agent.jar https://install.softprobe.ai/artifacts/agent/latest/sp-agent.jar
sp agent command --app --agent-jar ./sp-agent.jar --json # copy startCommand into your run script
sp record case list --app --since -1h --json
sp replay run --app --env http://your-service:8080 --from -24h --json
sp replay status --watch --json
sp diagnose replay --failed-only --out-dir .sp-work --json
```
## Related
* [Quickstart](/en/testing/getting-started)
* [For AI agents](/en/testing/agents/overview)
* [Examples](/en/testing/examples/agent-diagnose-replay)
* [API mapping](/en/testing/reference/api-mapping)
---
---
url: /en/testing/policy-yaml-guide.md
---
# Policy YAML guide
Softprobe Testing uses **declarative YAML policies** (`apiVersion: softprobe.ai/v1`) for recording, replay mocking, and diff comparison. Policies are versioned resources scoped to apps (and optionally environments and operations), merged by priority, and applied by sp-backend at runtime.
Manage the three CLI-supported kinds with:
```bash
sp policy recording validate -f recording.yaml --json
sp policy recording apply -f recording.yaml --json
sp policy mock apply -f mock.yaml --json
sp policy compare apply -f compare.yaml --json
```
| Kind | What it controls | CLI |
|------|------------------|-----|
| `RecordingPolicy` | Sampling, time windows, operation include/exclude, serialize skip, time mock | `sp policy recording` |
| `MockPolicy` | Skip/force mock, lookup tolerance, cross-app deps, fallback | `sp policy mock` |
| `CompareRulePolicy` | Ignore paths, decompress, transforms, array matchers, CEL validations | `sp policy compare` |
JSON schemas live in the backend module `sp-policy-rules` (`src/main/resources/schema/`). Use them in your editor via `# yaml-language-server: $schema=...` for completions.
## Shared document shape
Every policy has the same outer structure:
```yaml
apiVersion: softprobe.ai/v1
kind: RecordingPolicy # or MockPolicy / CompareRulePolicy
metadata:
name: my-app-recording
priority: 100
description: "optional"
enabled: true # RecordingPolicy / MockPolicy only
selector:
appIds: [my-app-id]
envTags:
env: [prod]
spec:
# kind-specific
```
### Common fields
| Field | Type | Required | Notes |
|-------|------|----------|-------|
| `apiVersion` | string | yes | Must be `softprobe.ai/v1` |
| `kind` | string | yes | One of the three kinds above |
| `metadata.name` | string | yes | Unique id (lowercase-dash) |
| `metadata.priority` | integer | no | Default `0`. Higher merges later and wins conflicts |
| `metadata.description` | string | no | Free text |
| `metadata.enabled` | boolean | no | Recording/Mock only; default `true` |
| `selector.matchAll` | boolean | no | Apply to every app (system defaults use this at priority 0) |
| `selector.appIds` | string\[] | one of\* | Exact app ids |
| `selector.appIdPattern` | string | one of\* | Glob, e.g. `order-*` |
| `selector.excludeAppIds` | string\[] | no | Subtract from match |
| `selector.envTags` | map | no | Tag key → list of allowed values, e.g. `env: [prod, staging]`. Matches agent tags from `-Dsp.mocker.tags=env=prod`. Conjunctive: every declared key must match. Omitted = all environments |
| `selector.operationNames` | string\[] | no | Exact operation names. **Recording and Mock only** — rejected on CompareRulePolicy selector |
| `selector.operationNamePatterns` | string\[] | no | Globs, e.g. `/api/order/*`. **Recording and Mock only** |
\*Selector must include at least one of `matchAll`, `appIds`, or `appIdPattern`.
**Selector dimensions** are conjunctive: app match AND (optional) env match AND (optional) operation match. Empty operation constraints mean “all operations for matched apps.”
### Merging
Multiple policies matching the same app are merged in **priority ascending** (higher number applied last):
| Kind | Merge behavior |
|------|----------------|
| **Compare** | Scalars overridden; lists/maps unioned; last-write-wins on keys |
| **Recording** | Scalars overridden; lists unioned; `timeMock` sticky-true (any policy sets true → true); `serializeSkip` merged by `className` + field name union |
| **Mock** | Scalars overridden; `skipMock`/`forceMock` unioned; `matchTolerance` and `multiServiceDependencies` keyed by pattern/app with last-write-wins |
Priority-0 `default-global-*-policy.yaml` files ship inside sp-backend; set `priority > 0` on your policies to override.
### Runtime pipeline
```text
YAML → parse → validate → Mongo → resolve + merge → compile → PolicyCache (fresh 60s, stale 24h)
```
If the cache cannot load from Mongo in time, callers degrade safely (recording: do not record; mock: pass-through).
### Authoring tips
1. Use JSON Schema in your editor for inline validation.
2. Avoid `matchAll: true` in app-specific policies unless you intend a global rule — defaults already provide a floor.
3. Comment the **why** in YAML; the schema documents the **what**.
***
## RecordingPolicy
Controls **what the agent records**: sampling, schedule, operation filters, optional scrubbing metadata, serialization skips, and record-time time mock.
### `spec` fields
| Section | Field | Type | Semantics |
|---------|-------|------|-----------|
| `sampling` | `ratePerHundredSeconds` | integer ≥ 0 | Max requests recorded per 100-second window. `0` = no recording |
| `sampling` | `machineCountLimit` | integer ≥ 1 or omit | Max concurrent recording instances in the env group. **Omit = unlimited.** Avoid `1` unless you understand quota pinning |
| `timeWindow` | `daysOfWeek` | `MON`…`SUN` or `*` | Omit section = 24/7 |
| `timeWindow` | `from` / `to` | `HH:mm` | Both required together; `from` strictly before `to` (validator). Agent uses JVM local timezone |
| `operations` | `exclude` | string\[] | Globs not recorded (deny list) |
| `operations` | `include` | string\[] | When non-empty: **whitelist** — only these ops recorded; `exclude` still applies after include |
| `sensitiveData` | `headers` | string\[] | Case-insensitive header names (see note below) |
| `sensitiveData` | `bodyPaths` | string\[] | JSONPath; must start with `$` |
| `sensitiveData` | `queryParams` | string\[] | Query parameter names |
| `sensitiveData` | `placeholder` | string | Replacement text; default `***` |
| `serializeSkip` | `className` | string | Java class |
| `serializeSkip` | `fieldNames` | string\[] | Fields to skip in bytecode serialization |
| | `timeMock` | boolean | Mock `java.time.*` at record time for deterministic replays |
| | `extras` | map string→string | Forwarded to agent `extendField` |
::: warning `sensitiveData` on the record path
The backend forwards `sensitiveData` to the agent wire DTO, but the **Java agent does not apply scrubbing at record time yet**. For replay mock-key noise, use `MockPolicy.spec.matchTolerance`. For view/query-time masking, use `SensitivePolicy` (REST API; no `sp policy` CLI today).
:::
Replay schedules read include/exclude from the compiled recording policy when building operation scope — changing recording policy can change replay scope without editing the schedule document.
### Full example
```yaml
apiVersion: softprobe.ai/v1
kind: RecordingPolicy
metadata:
name: order-service-prod
description: "Prod recording — 50 reqs/100s, business hours, scrub metadata."
priority: 100
enabled: true
selector:
appIds: [order-service]
envTags:
env: [prod]
operationNamePatterns:
- "/api/order/*"
spec:
sampling:
ratePerHundredSeconds: 50
machineCountLimit: 3
timeWindow:
daysOfWeek: [MON, TUE, WED, THU, FRI]
from: "09:00"
to: "18:00"
operations:
exclude:
- "/health"
- "/metrics/**"
- "/actuator/**"
# include:
# - "/api/order/create"
# - "/api/order/pay"
sensitiveData:
headers: [authorization, cookie, x-api-key]
bodyPaths:
- "$.password"
- "$.user.phone"
queryParams: [token, sessionId]
placeholder: "***"
serializeSkip:
- className: com.example.order.domain.Order
fieldNames: [internalNote]
timeMock: false
extras: {}
```
### Whitelist-only recording example
```yaml
apiVersion: softprobe.ai/v1
kind: RecordingPolicy
metadata:
name: order-service-whitelist
priority: 110
selector:
appIds: [order-service]
spec:
sampling:
ratePerHundredSeconds: 100
operations:
include:
- "/api/order/**"
exclude:
- "/api/order/internal/**"
```
***
## MockPolicy {#mockpolicy}
Controls **replay-time** dependency mocking: which calls use recorded data vs real downstream, mock-key tolerance, multi-app dependency maps, and behavior when no mock exists.
### `spec` fields
| Section | Field | Type | Semantics |
|---------|-------|------|-----------|
| | `mockByDefault` | boolean | Default `true`: mock all dependencies except `skipMock`. `false`: call real downstream except `forceMock` |
| `operations` | `skipMock` | string\[] | `Category:operationGlob` — bypass mock, hit real downstream |
| `operations` | `forceMock` | string\[] | `Category:operationGlob` — must mock; wins over `skipMock` |
| `matchTolerance` | `operationPattern` | string | Glob on **entry** operation |
| `matchTolerance` | `ignoreHeaders` | string\[] | Dropped from mock fingerprint |
| `matchTolerance` | `ignoreQueryParams` | string\[] | Dropped from mock fingerprint |
| `matchTolerance` | `ignoreBodyPaths` | string\[] | JSON paths ignored in mock lookup |
| `multiServiceDependencies` | `downstreamApp` | string | Other app id |
| `multiServiceDependencies` | `operations` | string\[] | Exact downstream ops to mock from session |
| `multiServiceDependencies` | `operationPatterns` | string\[] | Globs (one of `operations` or `operationPatterns` required) |
| `fallback` | `strategy` | enum | `FAIL` (default), `PASS_THROUGH`, `RETURN_DEFAULT` |
| `fallback` | `defaultResponse` | object | Required when `RETURN_DEFAULT`: `statusCode`, `contentType`, `body`, `headers` |
### Dependency categories for `skipMock` / `forceMock` {#dependency-categories}
Use **`Category:operationGlob`** — bare globs without a category prefix are **rejected**.
| Category | Typical `operationGlob` examples |
|----------|----------------------------------|
| `HttpClient` | `/payment/charge`, `/internal/**` |
| `Database` | `select_*`, `*` |
| `Redis` | `GET user:*` |
| `DubboConsumer` | `com.foo.Service#method` |
| `SofaConsumer` | RPC operation names |
| `DubboStreamProvider` | Stream ops |
| `QMessageProducer` | Topic or message id |
| `ConfigFile` | Config keys |
| `UserDynamic` | `com.foo.Cache.get` (user-configured dynamic class) |
| `DynamicClass` | `SystemTime.**`, `RandomSource.**` (built-in; global default force-mocks these — user `skipMock` has no effect) |
| `Encryption` | `com.foo.Crypto.encrypt` |
| `IbmMQ` | MQ destinations |
| `SoapClient` | SOAP actions |
| `DSR` | DSR operations |
Glob in the operation segment: `*` (one path segment), `**` (multiple segments), `?` (single character).
::: warning Entry types are not mock policy keys
Do not use `Servlet`, `DubboProvider`, or other **entry** categories in mock rules — only **dependency** categories above.
:::
### Full example
```yaml
apiVersion: softprobe.ai/v1
kind: MockPolicy
metadata:
name: order-service-replay
description: "Replay defaults for order-service"
priority: 100
enabled: true
selector:
appIds: [order-service]
envTags:
env: [staging, prod]
spec:
mockByDefault: true
operations:
skipMock:
- "HttpClient:/health"
- "HttpClient:/internal/admin/**"
forceMock:
- "HttpClient:/payment/charge"
- "Database:*"
matchTolerance:
- operationPattern: "/api/order/**"
ignoreHeaders: [x-request-id, x-trace-id, x-b3-*]
ignoreQueryParams: [_t, timestamp]
multiServiceDependencies:
- downstreamApp: payment-service
operations: ["/charge", "/refund"]
- downstreamApp: inventory-service
operationPatterns: ["/reserve", "/release"]
fallback:
strategy: FAIL
```
### Pass-through fallback example
```yaml
apiVersion: softprobe.ai/v1
kind: MockPolicy
metadata:
name: order-service-isolated-replay
priority: 100
selector:
appIds: [order-service]
spec:
fallback:
strategy: PASS_THROUGH
```
### `RETURN_DEFAULT` fallback example
```yaml
apiVersion: softprobe.ai/v1
kind: MockPolicy
metadata:
name: order-service-default-mock
priority: 100
selector:
appIds: [order-service]
spec:
operations:
forceMock:
- "HttpClient:/legacy/ping"
fallback:
strategy: RETURN_DEFAULT
defaultResponse:
statusCode: 200
contentType: application/json
body: '{"status":"ok"}'
headers:
x-mock: "true"
```
***
## CompareRulePolicy {#comparerulepolicy}
Controls **diff noise** during replay comparison: ignored paths, decompression, transforms, array matching, and post-diff CEL filters.
::: warning Operation scope
Do **not** put `operationNames` or `operationNamePatterns` on `selector` — the validator rejects them. Use `spec.operationSpecs[]` for per-operation overlays.
:::
### `spec` fields
| Section | Field | Type | Semantics |
|---------|-------|------|-----------|
| `defaults` | `timeToleranceMs` | integer ≥ 0 | Used by CEL `time_tolerance_ms` and timestamp rules |
| `defaults` | `ignoreHeaderPatterns` | string\[] | Header name globs skipped in compare |
| | `includePaths` | string\[] | JSON Pointer whitelist; empty = compare all |
| | `excludePaths` | string\[] | JSON Pointer blacklist (pre-filter) |
| `decompress[]` | `path` | string | JSON Pointer or glob (`/data/**`) |
| `decompress[]` | `codec` | enum | `PLAIN_JSON`, `BASE64_JSON`, `GZIP_BASE64_JSON` |
| `transforms[]` | `path` | string | JSON Pointer |
| `transforms[]` | `expression` | string | CEL expression to normalize value before compare |
| `arrays[]` | `path` | string | Array field path |
| `arrays[]` | `strategy` | enum | `BY_INDEX` (default) or `BY_KEY` |
| `arrays[]` | `keys` | string\[] | Required when `BY_KEY` |
| `arrays[]` | `references[]` | object | `field`, `target`, `targetKey` for FK-style array linking |
| `validations[]` | `id` | string | Unique rule id |
| `validations[]` | `name` | string | Display name |
| `validations[]` | `expression` | string | CEL; must evaluate to bool |
| `validations[]` | `action` | enum | `DROP` only (ignore this diff) |
| `validations[]` | `enabled` | boolean | Default `true` |
| `validations[]` | `message` | string | Human-readable reason |
| `operationSpecs[]` | `operationNames` / `operationNamePatterns` | string\[] | Which entry ops this overlay applies to |
| `operationSpecs[]` | `spec` | object | Same leaf fields as top-level spec (no nested `operationSpecs`) |
Paths use **JSON Pointer** syntax (`/foo/bar`). Globs: `*` = one segment, `**` = any depth.
### CEL variables and helpers
Available in `validations` and `transforms`:
| Name | Type | Meaning |
|------|------|---------|
| `left` | string | Recorded value |
| `right` | string | Replay value |
| `path` | string | JSON Pointer path |
| `pointer` | string | Alias for path |
| `fieldName` | string | Leaf field name |
| `category` | string | Dependency category, e.g. `DATABASE` |
| `time_tolerance_ms` | int | From `defaults.timeToleranceMs` |
| `isTimestamp(s)` | function | True if string is a known timestamp format |
| `toTimestamp(s)` | function | Epoch millis |
| `matches` | method | Regex on strings |
### Full example (app defaults + per-operation overlay)
```yaml
apiVersion: softprobe.ai/v1
kind: CompareRulePolicy
metadata:
name: order-service-compare
description: "Compare rules for order-service"
priority: 100
selector:
appIds: [order-service]
spec:
defaults:
timeToleranceMs: 60000
ignoreHeaderPatterns: [x-request-id, x-b3-*]
excludePaths:
- "/response/headers/date"
- "/response/body/metadata/generatedAt"
decompress:
- path: "/response/body/payload"
codec: GZIP_BASE64_JSON
arrays:
- path: "/response/body/items"
strategy: BY_KEY
keys: [skuId]
validations:
- id: ignore-uuids
name: Ignore UUID pairs
expression: >-
left.matches("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$")
&& right.matches("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$")
action: DROP
enabled: true
message: Both sides are UUIDs
- id: ignore-db-body
name: Skip raw SQL body on DATABASE
expression: 'category == "DATABASE" && fieldName == "body"'
action: DROP
enabled: true
operationSpecs:
- operationNamePatterns: ["/api/order/export"]
spec:
excludePaths:
- "/response/body/exportJobId"
validations:
- id: export-timestamp-tolerance
expression: >-
isTimestamp(left) && isTimestamp(right)
&& math.abs(toTimestamp(left) - toTimestamp(right)) <= time_tolerance_ms
action: DROP
enabled: true
```
***
## Related configuration {#related-configuration}
### Dynamic classes
**Not** part of `RecordingPolicy`. Configure methods in **Dynamic class configuration** (dashboard or storage API). At replay, they appear under `UserDynamic` or built-in `DynamicClass` (e.g. `SystemTime.*`, `RandomSource.*`). Control mock behavior with `MockPolicy` `forceMock` / `skipMock`, not recording policy.
Matching at replay: exact parameter match first, then fuzzy match by signature.
### SensitivePolicy
Fourth policy kind for **query/view-time** PII masking (REST API; no `sp policy` CLI). Separate from `RecordingPolicy.spec.sensitiveData`.
| Field | Notes |
|-------|-------|
| `spec.fieldNameRules[]` | `pattern` (Java regex), `type`: `NAME`, `ID_CARD`, `PHONE`, `EMAIL`, `PASSPORT`, `DEFAULT`, `NONE` |
| `spec.contentRules[]` | Same shape; matches field **values** |
```yaml
apiVersion: softprobe.ai/v1
kind: SensitivePolicy
metadata:
name: order-service-sensitive
priority: 100
selector:
appIds: [order-service]
spec:
fieldNameRules:
- pattern: "(?i)^password$"
type: NAME
contentRules:
- pattern: "^1[3-9]\\d{9}$"
type: PHONE
```
## Related
* [Policies overview](/en/testing/policies)
* [Replay and diff](/en/testing/replay-and-diff)
* [CLI: policy command](/en/testing/commands/policy)
---
---
url: /en/testing/installation/configuration.md
---
# Client configuration
This page is the reference for client-side (`sp` CLI, Java agent, coding engine) config files and advanced options. Softprobe uses one XDG namespace shared by `sp`, `sp code`, and the Java agent.
| Path | Purpose |
|------|---------|
| `~/.config/softprobe/config.jsonc` | Shared backend URL and credentials |
| `~/.config/softprobe/sp.jsonc` | CLI profiles and overrides |
| `~/.config/softprobe/spcode.jsonc` | Internal coding engine settings |
| `~/.local/share/softprobe/agent/sp-agent.jar` | Installed Java agent |
| `~/.local/share/softprobe/bin/spcode` | Internal coding engine binary |
Use `sp setup` for the backend URL. Use `sp code` for coding-engine and model-provider readiness.
## Spcode Service (Linux systemd)
When you install Spcode Service with [`sp setup --install-spcode-service`](./#spcode-service), the shared workbench uses **the same Softprobe settings as the account that ran install** (backend URL, MCP, agent instructions, skills).
Configure Softprobe as that install account. After you change settings the service should pick up, restart it:
```bash
sudo systemctl restart spcode-web.service
```
## MCP tools and agent instructions {#spcode-service-mcp-agents}
`sp setup` / `sp code` do **not** create coding-engine MCP settings or global agent instructions. Add them under:
| File | Purpose |
|------|---------|
| `~/.config/spcode/opencode.jsonc` | MCP servers and engine settings |
| `~/.config/spcode/AGENTS.md` | Global agent instructions |
These paths apply to both personal `sp code web` and Linux Spcode Service. Restart the UI process (or `spcode-web.service`) after you edit them.
### Example: Feishu / Lark MCP + `AGENTS.md`
::: warning Prerequisite: Node.js
This MCP example launches `@larksuiteoapi/lark-mcp` with **`npx`**, so the host must have **Node.js** installed (which provides `npx`). Softprobe install does **not** install Node.js for you.
Check:
```bash
node -v
npx -v
```
If those commands are missing, install Node.js (LTS) from [nodejs.org](https://nodejs.org/), or with your OS package manager (for example `apt install nodejs npm` on Debian/Ubuntu, or `dnf install nodejs` on RHEL/Fedora). For Spcode Service, confirm `npx -v` also works when run as root (`sudo npx -v`), because the service process runs as root.
:::
1. Create `~/.config/spcode/opencode.jsonc` (replace placeholders with your app credentials; never commit real secrets):
```jsonc
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"lark-mcp": {
"type": "local",
"enabled": true,
"command": [
"npx",
"-y",
"@larksuiteoapi/lark-mcp",
"mcp",
"-a",
"YOUR_FEISHU_APP_ID",
"-s",
"YOUR_FEISHU_APP_SECRET",
"--token-mode",
"tenant_access_token",
"--domain",
"https://open.feishu.cn",
"-t",
"docx.v1.document.get,docx.v1.document.rawContent"
]
}
}
}
```
The host also needs network access to Feishu/Lark (`https://open.feishu.cn`).
2. Create `~/.config/spcode/AGENTS.md` so agents know how to use that MCP. Example (Chinese customer):
```md
# Softprobe 服务端 Agent 说明
关于应用、Git 分支、录制/回放仓库信息,**不要硬编码**。
请使用 **lark-mcp** MCP 服务,从以下飞书文档获取最新内容:
https://example.feishu.cn/docx/YOUR_DOC_TOKEN
优先调用 `docx.v1.document.rawContent`(或 lark-mcp 中的等价工具)读取该文档,并以返回的表格/文本作为应用名称与分支配置的唯一真实来源。
```
Replace the Feishu doc URL with your own. You can point agents at any MCP you configure in `opencode.jsonc`, not only Feishu.
## Skills {#skills}
Skills are folders with a `SKILL.md` file. Softprobe loads them automatically from:
| Location | Path |
|----------|------|
| Global (this machine) | `~/.config/spcode/skill//SKILL.md` or `~/.config/spcode/skills//SKILL.md` |
| Project | `.opencode/skill//SKILL.md` or `.opencode/skills//SKILL.md` in the opened repo |
Each `SKILL.md` needs frontmatter with at least `name` and `description`. Example:
```md
---
name: my-skill
description: Use when the user asks about release checklists.
---
# My skill
Steps the agent should follow…
```
To load skills from another directory, set `skills.paths` in `~/.config/spcode/opencode.jsonc`.
There is no separate “create skill” command — add the folder and file yourself (or ask the coding agent to help). After changing global skills used by Spcode Service, restart `spcode-web.service`.
---
---
url: /en/testing/reference/exit-codes.md
---
# Exit codes
Contract for agents and CI scripts invoking `sp`.
| Code | Name | Meaning |
|------|------|---------|
| `0` | Success | Parsed JSON on stdout when `--json` |
| `1` | API\_ERROR | Backend returned error or HTTP non-success (includes `NO_RECORDED_CASES` on `replay run`) |
| `2` | USAGE / PROFILE\_NOT\_FOUND / CONFIG\_\* | Invalid flags, missing config, parse errors, unknown profile |
| `3` | AUTH\_REQUIRED | No token available in non-interactive mode |
## stderr with `--json`
On exit `1` or `2` or `3`, stderr contains a JSON error object:
```json
{
"ok": false,
"command": "app list",
"error": {
"code": "AUTH_REQUIRED",
"message": "Set SP_TOKEN or run sp auth login"
}
}
```
## Config error codes (exit `2`)
| Code | Cause |
|------|-------|
| `CONFIG_MISSING` | No `config.jsonc` / `sp.jsonc`; run `sp config init` |
| `CONFIG_PARSE_ERROR` | Invalid JSONC or schema validation failure |
| `CONFIG_WRITE_ERROR` | Could not write config file |
| `PROFILE_NOT_FOUND` | Selected profile missing from merged config |
## Agent retry guidance
| Code | Retry? |
|------|--------|
| 0 | No |
| 1 | Sometimes — after fixing data or if transient 5xx |
| 2 | No — fix invocation |
| 3 | After refresh/login |
---
---
url: /en/testing/reference/json-types.md
---
# JSON types
CLI-level envelope: see [Output contract](/en/testing/agents/output-contract).
## Common `data` shapes
### ApplicationListItem
Used in `sp app list` → `data.items[]`.
```json
{
"appId": "string",
"appName": "string",
"name": "string",
"agentStatus": "online | offline | never",
"lastSeenAt": 0,
"agentVersion": "string",
"env": "string",
"tags": ["string"],
"worktreeDirectory": "string"
}
```
### ApplicationCreateResult
Used in `sp app create` → `data`.
```json
{
"success": true,
"appId": "string",
"msg": "string"
}
```
### AgentStatus
Used in `sp app status` → `data`.
```json
{
"appId": "string",
"status": "online | offline | never",
"instanceCount": 0,
"lastSeenAt": 0,
"agentVersion": "string"
}
```
### PolicyValidateResult
```json
{
"valid": true,
"errors": [{ "path": "spec.sampling.rate", "message": "..." }],
"warnings": []
}
```
### ReplayPlanCreated
```json
{
"planId": "string",
"result": 1,
"desc": "string"
}
```
### ReplayProgress
```json
{
"planId": "string",
"status": "RUNNING | FINISHED | FAILED | CANCELLED",
"percent": 0,
"finished": false
}
```
### TraceSummary
```json
{
"traceId": "string",
"endpoint": "string",
"status": "string",
"durationMs": 0,
"startedAt": "ISO-8601",
"attrs": { "orderId": "ORD-123" }
}
```
### ArtifactResult
```json
{
"artifact": "relative/path.json",
"summary": {}
}
```
### PaginatedList
```json
{
"items": [],
"page": 1,
"pageSize": 20,
"total": 0,
"hasMore": false
}
```
## Backend passthrough
When `--include-backend` (optional debug flag) is set, implementers may add `data._backend` with the raw `Response.body` or `CommonResponse.data`.
Agents should not depend on `_backend` in production skills.
## schemaVersion
Future CLI releases may add top-level `"schemaVersion": 1` in the envelope. Agents must ignore unknown top-level fields.
---
---
url: /en/testing/reference/api-mapping.md
---
# API mapping
Authoritative **REST ↔ CLI** reference for sp-backend (default port **8090**). Header `access-token` required on console APIs unless noted.
**Legend:** Category `platform` | `investigation` | `admin` | `—` (not exposed)
## Auth
| Method | Path | CLI | Category |
|--------|------|-----|-------|
| GET | `/api/login/getVerificationCode/{userName}` | (precursor to login) | platform |
| POST | `/api/login/verify` | `sp auth login` | platform |
| GET | `/api/login/refresh/{userName}` | `sp auth refresh` | platform |
| POST | `/api/login/loginAsGuest` | `sp auth login --guest` | platform |
| POST | `/api/login/oauthLogin` | — | — |
| GET | `/api/login/userProfile/{userName}` | `sp auth whoami` (future) | investigation |
## Applications
| Method | Path | CLI | Category |
|--------|------|-----|-------|
| GET | `/api/applications/list` | `sp app list` | platform |
| POST | `/api/applications/create` | `sp app create` | platform |
| GET | `/api/applications/{appId}/agent-status` | `sp app status` | platform |
| GET | `/api/applications/{appId}/replays/recent` | `sp app replays` | platform |
| GET | `/api/applications/{appId}/extraction-rules` | `sp extraction-rule list` | investigation |
| PUT | `/api/applications/{appId}/extraction-rules` | `sp extraction-rule apply` | investigation |
| POST | `/api/extraction-rules/preview` | `sp extraction-rule preview` | investigation |
| GET | `/api/agent/extraction-rules` | — (agent) | — |
## Policies
| Method | Path | CLI | Category |
|--------|------|-----|-------|
| GET | `/api/recording-policies/policies` | `sp policy recording list` | platform |
| GET | `/api/recording-policies/policies/{id}` | `sp policy recording get` | platform |
| POST | `/api/recording-policies/policies` | `sp policy recording apply` | platform |
| POST | `/api/recording-policies/policies/yaml` | `sp policy recording apply -f` | platform |
| DELETE | `/api/recording-policies/policies/{id}` | `sp policy recording delete` | platform |
| POST | `/api/recording-policies/policies/validate` | `sp policy recording validate` | platform |
| GET | `/api/recording-policies/policies/{id}/yaml` | `sp policy recording export` | platform |
| \* | `/api/mock-policies/*` | `sp policy mock …` | platform |
| \* | `/api/compare-rules/*` | `sp policy compare …` | platform |
## Replay control (schedule)
| Method | Path | CLI | Category |
|--------|------|-----|-------|
| POST | `/api/createPlan` | `sp replay run` (`targetEnv` = service base URL) | platform |
| GET | `/api/createPlan` | — (webhook only) | — |
| GET | `/api/progress` | `sp replay status` | platform |
| GET | `/api/stopPlan` | `sp replay stop` | platform |
| POST | `/api/reRunPlan` | `sp replay rerun` | platform |
| POST | `/api/createRealTimePlan` | `sp replay realtime create` | investigation |
| GET | `/api/queue/control/*` | `sp replay realtime queue …` | investigation |
| POST | `/api/compareCase` | `sp replay compare` | investigation |
| GET | `/api/queryNoise` | `sp replay noise query` | investigation |
| POST | `/api/excludeNoise` | `sp replay noise exclude` | investigation |
## Traces
| Method | Path | CLI | Category |
|--------|------|-----|-------|
| POST | `/api/traces/find-by-attr` | `sp trace find` | investigation |
| GET | `/api/traces/{traceId}/summary` | `sp trace get` | investigation |
| POST | `/api/traces/attr-rule-statistics` | `sp trace stats` | investigation |
## Storage record (read)
| Method | Path | CLI | Category |
|--------|------|-----|-------|
| POST | `/api/storage/record/query` | `sp record query` | investigation |
| GET | `/api/storage/record/trace/{traceId}` | `sp record trace` | investigation |
| GET | `/api/storage/record/completeness` | `sp record completeness` | investigation |
| POST | `/api/storage/record/save` | — | — |
| \* | `/api/storage/replay/query/*` | `sp replay case list` (partial) | investigation |
| GET | `/api/storage/replay-mock-tree/{replayId}` | `sp replay mock-tree` | investigation |
## Logs
| Method | Path | CLI | Category |
|--------|------|-----|-------|
| GET | `/api/record-logs/overview` | `sp record logs overview` | investigation |
| GET | `/api/record-logs/download` | `sp record logs download` | investigation |
| GET | `/api/replay-logs/overview` | `sp replay logs overview` | investigation |
| GET | `/api/replay-logs/download` | `sp replay logs download` | investigation |
## Reports (subset)
| Method | Path | CLI | Category |
|--------|------|-----|-------|
| GET | `/api/report/queryDiffMsgById/{id}` | `sp replay diff get` | investigation |
| POST | `/api/report/queryReplayCase` | `sp replay case list` | investigation |
| POST | `/api/report/init` | `sp replay report init` | investigation |
| \* | `/api/report/*` (others) | partial / future | investigation |
## Agent config
| Method | Path | CLI | Category |
|--------|------|-----|-------|
| POST | `/api/config/agent/load` | `sp config agent load` | investigation |
| POST | `/api/config/agent/agentStatus` | — or `sp app status` | investigation |
## Legacy config
| Method | Path | CLI | Category |
|--------|------|-----|-------|
| GET/POST | `/api/config/{resource}/*` | `sp config legacy …` | investigation |
## User groups
| Method | Path | CLI | Category |
|--------|------|-----|-------|
| \* | `/api/userGroup/*` | `sp group …` | investigation |
| \* | `/api/appGrant/*` | `sp grant …` | investigation |
## Health & ops
| Method | Path | CLI | Category |
|--------|------|-----|-------|
| GET | `/vi/health` | `sp health` | platform |
| GET | `/vi/storage/*` | `sp ops storage …` | investigation |
| GET | `/vi/schedule/monitor` | `sp ops schedule monitor` | investigation |
## Not exposed
| Method | Path | Reason |
|--------|------|--------|
| POST | `/v1/traces` | OTLP |
| POST | `/api/storage/record/save` | Agent write |
| \* | `/api/replay/local/*` | Local runner |
Controller source: `sp-tr-api/sp-web-api`, `sp-storage/sp-storage-web-api`, `sp-replay-schedule/sp-schedule-web-api`.
---
---
url: /en/testing/reference/log-correlation-ids.md
---
# Log correlation IDs
When a replay fails, you need a **`trace_id`** and a bounded time window to pull correlated logs from the application under test, the Java agent, and sp-backend in one query. This page explains what each id means, **where to find it**, and **how to triage** unified log results.
Requires the unified log pipeline (Vector → Parquet) enabled in your deployment or local compose stack. If the pipeline is disabled, log lookups fail fast with a clear error — they do not fall back to other log stores.
**Command reference:** [sp logs](/en/testing/commands/logs) · **API:** `GET /api/recorder/logs?trace_id=…&since=…&until=…`
***
## ID quick reference
| ID | v1 log lookup? | What it identifies | Use for log search |
|----|----------------|--------------------|--------------------|
| **`traceId`** | **Yes** (`--trace-id`) | W3C OpenTelemetry trace for one request flow | **Only v1 lookup key** — all agent/app/backend lines on that trace in `[since, until)` |
| **`replayId`** | No | One replay **attempt** of a recorded case | Find failed case + diff; copy **`traceId`** from the same row for logs |
| **`planId`** | No | A replay **plan** (batch from `sp replay run`) | Scope diagnose / case list; copy per-case **`traceId`** for logs |
| **`planItemId`** | No | One **case/operation** inside a plan | Same — use with case list, not as log key |
| **`diffId`** | No | Compare/diff result row | Use with `sp replay diff get`, not for logs |
Each log row carries **`source`**: `agent` (Java agent diagnostics), `app` (application-under-test logs captured by the agent), or `backend` (sp-backend diagnostics). API/Parquet use unprefixed column names (`source`, `replay_id`, …). OTLP wire format may use `sp.*` keys before Vector normalization.
**Not in v1 log query results:** `sessionId` / `sp.session_id`.
**Rejected in v1:** `--replay-id`, `--plan-id`, `--plan-item-id`, `--include-recording-log`, `GET /api/record-logs/*`, `GET /api/replay-logs/*`.
***
## Where to find `traceId` (primary for logs)
| Source | How |
|--------|-----|
| Failed replay case list | `sp replay case list --plan --failed --json` → `data.items[].traceId` |
| Replay metadata | `sp replay metadata --json` → `traceId` |
| Diagnose workflow | `sp diagnose replay --failed-only --json` → then case list for `traceId` |
| CI / `make e2e` | **Softprobe correlation** block under pytest failure → field `trace_id` per case |
| Recorded cases | `sp record case list --app --since -24h --json` → entry case trace ids |
**Important:** For replay failures, use the **`traceId` on the replay case** (same value as the recorded trace — schedule puts it on `traceparent`). Do **not** guess from the newest recording list page or health-check traffic (`/index.html`, `/`) — those traces are unrelated noise.
`replayId`, `planId`, and `planItemId` in pytest output are for human reference and diff/diagnose commands only — not v1 log lookup keys.
***
## Query correlated logs
Every lookup requires **`trace_id`** plus **`since`** and **`until`**. Use **ISO-8601 UTC** (for example `2026-06-27T10:00:00Z`). `since` is **inclusive**; `until` is **exclusive** (`[since, until)`).
### CLI (primary)
```bash
sp logs --trace-id --since --until [--json]
```
Add **`--json`** for scripts and AI agents. Human-readable text is the default.
### HTTP API (when `sp` is not in PATH)
```bash
export SP_API_URL="${SP_API_URL:-http://127.0.0.1:18090}"
TRACE_ID="2057ad46a7ce03d3955385f2a4142d29"
SINCE="2026-06-27T10:00:00Z"
UNTIL="2026-06-27T10:05:00Z"
curl -s "${SP_API_URL}/api/recorder/logs?trace_id=${TRACE_ID}&since=${SINCE}&until=${UNTIL}" \
-H "Accept: application/json" -o /tmp/trace-logs.json
```
### Picking `since` / `until`
**When you have case timestamps (`recordTime` and `replayTime`):** run **two** ±2 minute lookups — one around each anchor — and merge rows. Never bridge record time to replay time in a single query. See [sp logs — Case-scoped lookup](/en/testing/commands/logs#case-scoped-lookup-dual-windows).
**Otherwise:**
1. Start with a window around the failure (for example five minutes before plan finish through one minute after).
2. Widen the window if row counts show zeros for a `source` you expect (`agent`, `app`, `backend`).
3. E2E after `make compose-parquet-clean`: rerun the session, then poll up to ~180s for Vector ingest.
4. v1 returns **all** matching rows in the window — redirect or pipe locally for large output:
```bash
sp logs --trace-id --since … --until … > /tmp/trace.log
grep ERROR /tmp/trace.log | head -20
```
There is no `--limit` on v1 log lookups.
***
## Triage unified log results
After fetching logs, follow this order.
**`sp --json logs`** wraps the API body in `.data` — use `jq '.data.rows'`, `jq '.data.warnings'`. **`curl`** saves the API JSON directly — use `jq '.rows'`, `jq '.warnings'`.
```bash
# After sp --json logs (CLI envelope)
jq '.data.rows | length' /tmp/trace-logs.json
jq '[.data.rows[].source] | group_by(.) | map({source: .[0], n: length})' /tmp/trace-logs.json
jq '.data.warnings' /tmp/trace-logs.json
jq -r '.data.rows[] | select(.source=="backend" and .severity=="ERROR") | "\(.timestamp) \(.body)"' /tmp/trace-logs.json | head -20
# After curl GET /api/recorder/logs (API body at top level)
jq '.rows | length' /tmp/trace-logs.json
jq '[.rows[].source] | group_by(.) | map({source: .[0], n: length})' /tmp/trace-logs.json
jq '.warnings' /tmp/trace-logs.json
jq -r '.rows[] | select(.source=="backend" and .severity=="ERROR") | "\(.timestamp) \(.body)"' /tmp/trace-logs.json | head -20
```
### Symptom guide
| What you see | Likely meaning | Next step |
|--------------|----------------|-----------|
| `rows` empty + `warnings` non-empty | Parquet reader/schema mismatch (e.g. stale backend image) | Rebuild sp-backend; confirm API rows use `source` not `sp.source` |
| `rows` empty + `warnings` empty | Wrong `trace_id`, narrow window, or ingest lag | Use replay case `traceId`; widen `[since, until)`; rerun replay |
| Rows from `agent`, `app`, and `backend` | Pipeline healthy for that trace | Read `body` for ERROR/WARN; use `sp diagnose` diffs for compare failures |
| Only `backend`, no `app`/`agent` | Agent export or app logging quiet | Check agent attach, `sp.enable.debug`, app logger levels |
Recording-phase and replay-phase lines for the **same business request** share one **`trace_id`** on replay (recorded trace reused on `traceparent`). A single trace lookup can include both without `--include-recording-log` (removed in v1).
***
## Reading the response
**HTTP API** top-level fields:
* `lookup` — type `trace`, value, and caller `[since, until)` bounds
* `rows[]` — each row: `timestamp`, `severity`, `body`, `service_name`, `source`, and optional `trace_id`, `span_id`, `replay_id`, `plan_id`, `plan_item_id`
* `warnings` — non-fatal schema-skip or similar (may be empty)
**`sp --json logs`** returns a CLI envelope: `{"ok":true,"command":"logs","data":{...}}` — use `.data.rows` and `.data.warnings` in scripts.
v1 responses do **not** include `source_summary`. Compute per-source counts locally with `jq` (see above).
See [Log query fields](/en/testing/commands/log-query-fields) for the full field reference.
***
## Pytest / `make e2e` failures
On replay-related test failures, pytest prints:
1. **Softprobe correlation** — `trace_id` (use for log query), plus `replay_id`, `plan_id`, `plan_item_id` for reference.
2. **Unified logs** — optional summary: row count and per-`source` counts when the hook ran curl.
Copy `trace_id` and the suggested `since`/`until` from the block, then run the triage commands above before diving into application code.
***
## Typical failure workflow
```text
1. sp replay case list --plan --failed --json
→ copy traceId (and replayId for diff/diagnose)
2. sp logs --trace-id --since … --until … [--json]
→ triage: count → sources → warnings → read backend/agent/app bodies
(curl GET /api/recorder/logs only when sp is unavailable)
3. sp diagnose replay --failed-only --out-dir .sp-work --json
→ read diff artifacts for field-level compare failures
4. If logs empty after clean Parquet: rerun replay/e2e and poll ingest
```
***
## API equivalent
```http
GET /api/recorder/logs?trace_id=&since=&until=
```
Unsupported query parameters (`replay_id`, `plan_id`, `plan_item_id`, `include_recording_log`, …) are rejected before Parquet reads.
See [sp logs](/en/testing/commands/logs) for validation rules and JSON shape.
***
## Related
* [Concepts — replay plans and ids](/en/testing/agents/concepts#trace-replay-and-plan-ids)
* [Diagnose replay failure example](/en/testing/examples/agent-diagnose-replay)
* [sp replay case](/en/testing/commands/replay-case)
* [sp trace](/en/testing/commands/trace)
---
---
url: /en/testing/reference/replay-send-log-markers.md
---
# Replay send log markers
**When agents use this:** Interpret sp-backend schedule logs around each replay HTTP dispatch — the critical entry and exit boundary between replay scheduling and your application.
These structured lines are emitted by **sp-schedule** (`DefaultHttpReplaySender`) immediately before and after each entry HTTP call. They appear in correlated log lookups (`sp logs --trace-id …`, unified trace log downloads) when `sp.source` is **`backend`** and `service_name` is typically **`sp-backend`**.
Query logs with the case **`traceId`** (same value as `recordedTraceId` in the message when correlation succeeded). See [sp logs command](../commands/logs.md).
***
## Message prefixes
| Prefix | Severity | Meaning |
|--------|----------|---------|
| `Replay send start:` | INFO | Schedule is about to invoke the recorded entry HTTP request |
| `Replay send done:` | INFO | HTTP exchange completed; response status and duration are recorded |
| `Replay send failed:` | WARN | HTTP exchange failed before a normal completion |
Each prefix is followed by comma-separated **`key=value`** pairs (SLF4J structured message, not JSON).
***
## Structured fields
| Field | On | Description |
|-------|-----|-------------|
| `planId` | start, done, failed | Replay **plan** id for the batch run |
| `targetEnv` | start, done, failed | `true` when sending to the configured replay test URL (`--env` / `targetEnv`); `false` for record-side sends |
| `method` | start, done, failed | HTTP method (`GET`, `POST`, …) |
| `url` | start, done, failed | Full URL schedule invoked (path and query string included) |
| `recordedTraceId` | start, done, failed | W3C trace id from the **recording**; should match `trace_id` on the log row when correlation succeeded |
| `spanId` | start, done, failed | OpenTelemetry span id active on the send path |
| `httpStatus` | done, failed | HTTP status code from the response, or **`-1`** when no response was received (connection error, timeout, DNS failure) |
| `durationMs` | done, failed | Wall-clock milliseconds for the HTTP exchange |
| `error` | failed only | Exception or error message when the send did not complete successfully |
***
## Example lines
```text
Replay send start: planId=6a3f2aad59f0c4655b0f99da, targetEnv=true, method=POST, url=http://order-service.test:8080/api/orders, recordedTraceId=2057ad46a7ce03d3955385f2a4142d29, spanId=8d10c94a2a6f4e11
Replay send done: planId=6a3f2aad59f0c4655b0f99da, targetEnv=true, method=POST, url=http://order-service.test:8080/api/orders, recordedTraceId=2057ad46a7ce03d3955385f2a4142d29, spanId=8d10c94a2a6f4e11, httpStatus=200, durationMs=142
Replay send failed: planId=6a3f2aad59f0c4655b0f99da, targetEnv=true, method=POST, url=http://order-service.test:8080/api/orders, recordedTraceId=2057ad46a7ce03d3955385f2a4142d29, spanId=8d10c94a2a6f4e11, httpStatus=-1, durationMs=30001, error=Connection refused
```
***
## How to use them
1. Obtain **`traceId`** from `sp replay case list --plan --failed --json` or replay metadata.
2. Pull correlated logs for a time window around the failure (see [Diagnose replay failure example](../examples/agent-diagnose-replay.md)).
3. Filter **`sp.source=backend`** rows whose **`body`** contains `Replay send start`, `Replay send done`, or `Replay send failed`.
| Observation | Likely cause |
|-------------|--------------|
| No `Replay send start` in the window | Case may not have reached HTTP send (preload, schedule, or plan cancellation) — check plan status first |
| `start` without matching `done` or `failed` | Send may still be in flight, or logs were truncated — widen the time window |
| `failed` with `httpStatus=-1` | Network or timeout reaching **`targetEnv`** — verify `--env` URL, DNS, firewall, and that the app is running |
| `failed` with 4xx/5xx | Application returned an error HTTP status — inspect agent and application logs after the `start` timestamp |
| `done` with 2xx but compare still fails | Entry request reached the app; investigate agent mock/compare behavior after the `done` line |
***
## Related ERROR lines (same send attempt)
These are **not** the entry/exit markers but often appear on the same failed send:
| Message prefix | Severity | Meaning |
|----------------|----------|---------|
| `Replay send error:` | ERROR | Stack trace after a failed send (follows `Replay send failed`) |
| `Replay send result invalid:` | ERROR | Response received but missing expected trace/replay headers for result extraction |
***
## Related
* [Replay and diff](/en/testing/replay-and-diff)
* [Diagnose replay failure example](../examples/agent-diagnose-replay.md)
* [sp logs command](../commands/logs.md)
---
---
url: /zh/testing/reference/api-mapping.md
---
# API mapping
Authoritative **REST ↔ CLI** reference for sp-backend (default port **8090**). Header `access-token` required on console APIs unless noted.
**Legend:** Category `platform` | `investigation` | `admin` | `—` (not exposed)
## Auth
| Method | Path | CLI | Category |
|--------|------|-----|-------|
| GET | `/api/login/getVerificationCode/{userName}` | (precursor to login) | platform |
| POST | `/api/login/verify` | `sp auth login` | platform |
| GET | `/api/login/refresh/{userName}` | `sp auth refresh` | platform |
| POST | `/api/login/loginAsGuest` | `sp auth login --guest` | platform |
| POST | `/api/login/oauthLogin` | — | — |
| GET | `/api/login/userProfile/{userName}` | `sp auth whoami` (future) | investigation |
## Applications
| Method | Path | CLI | Category |
|--------|------|-----|-------|
| GET | `/api/applications/list` | `sp app list` | platform |
| POST | `/api/applications/create` | `sp app create` | platform |
| GET | `/api/applications/{appId}/agent-status` | `sp app status` | platform |
| GET | `/api/applications/{appId}/replays/recent` | `sp app replays` | platform |
| GET | `/api/applications/{appId}/extraction-rules` | `sp extraction-rule list` | investigation |
| PUT | `/api/applications/{appId}/extraction-rules` | `sp extraction-rule apply` | investigation |
| POST | `/api/extraction-rules/preview` | `sp extraction-rule preview` | investigation |
| GET | `/api/agent/extraction-rules` | — (agent) | — |
## Policies
| Method | Path | CLI | Category |
|--------|------|-----|-------|
| GET | `/api/recording-policies/policies` | `sp policy recording list` | platform |
| GET | `/api/recording-policies/policies/{id}` | `sp policy recording get` | platform |
| POST | `/api/recording-policies/policies` | `sp policy recording apply` | platform |
| POST | `/api/recording-policies/policies/yaml` | `sp policy recording apply -f` | platform |
| DELETE | `/api/recording-policies/policies/{id}` | `sp policy recording delete` | platform |
| POST | `/api/recording-policies/policies/validate` | `sp policy recording validate` | platform |
| GET | `/api/recording-policies/policies/{id}/yaml` | `sp policy recording export` | platform |
| \* | `/api/mock-policies/*` | `sp policy mock …` | platform |
| \* | `/api/compare-rules/*` | `sp policy compare …` | platform |
## Replay control (schedule)
| Method | Path | CLI | Category |
|--------|------|-----|-------|
| POST | `/api/createPlan` | `sp replay run` (`targetEnv` = service base URL) | platform |
| GET | `/api/createPlan` | — (webhook only) | — |
| GET | `/api/progress` | `sp replay status` | platform |
| GET | `/api/stopPlan` | `sp replay stop` | platform |
| POST | `/api/reRunPlan` | `sp replay rerun` | platform |
| POST | `/api/createRealTimePlan` | `sp replay realtime create` | investigation |
| GET | `/api/queue/control/*` | `sp replay realtime queue …` | investigation |
| POST | `/api/compareCase` | `sp replay compare` | investigation |
| GET | `/api/queryNoise` | `sp replay noise query` | investigation |
| POST | `/api/excludeNoise` | `sp replay noise exclude` | investigation |
## Traces
| Method | Path | CLI | Category |
|--------|------|-----|-------|
| POST | `/api/traces/find-by-attr` | `sp trace find` | investigation |
| GET | `/api/traces/{traceId}/summary` | `sp trace get` | investigation |
| POST | `/api/traces/attr-rule-statistics` | `sp trace stats` | investigation |
## Storage record (read)
| Method | Path | CLI | Category |
|--------|------|-----|-------|
| POST | `/api/storage/record/query` | `sp record query` | investigation |
| GET | `/api/storage/record/trace/{traceId}` | `sp record trace` | investigation |
| GET | `/api/storage/record/completeness` | `sp record completeness` | investigation |
| POST | `/api/storage/record/save` | — | — |
| \* | `/api/storage/replay/query/*` | `sp replay case list` (partial) | investigation |
| GET | `/api/storage/replay-mock-tree/{replayId}` | `sp replay mock-tree` | investigation |
## Logs
| Method | Path | CLI | Category |
|--------|------|-----|-------|
| GET | `/api/record-logs/overview` | `sp record logs overview` | investigation |
| GET | `/api/record-logs/download` | `sp record logs download` | investigation |
| GET | `/api/replay-logs/overview` | `sp replay logs overview` | investigation |
| GET | `/api/replay-logs/download` | `sp replay logs download` | investigation |
## Reports (subset)
| Method | Path | CLI | Category |
|--------|------|-----|-------|
| GET | `/api/report/queryDiffMsgById/{id}` | `sp replay diff get` | investigation |
| POST | `/api/report/queryReplayCase` | `sp replay case list` | investigation |
| POST | `/api/report/init` | `sp replay report init` | investigation |
| \* | `/api/report/*` (others) | partial / future | investigation |
## Agent config
| Method | Path | CLI | Category |
|--------|------|-----|-------|
| POST | `/api/config/agent/load` | `sp config agent load` | investigation |
| POST | `/api/config/agent/agentStatus` | — or `sp app status` | investigation |
## Legacy config
| Method | Path | CLI | Category |
|--------|------|-----|-------|
| GET/POST | `/api/config/{resource}/*` | `sp config legacy …` | investigation |
## User groups
| Method | Path | CLI | Category |
|--------|------|-----|-------|
| \* | `/api/userGroup/*` | `sp group …` | investigation |
| \* | `/api/appGrant/*` | `sp grant …` | investigation |
## Health & ops
| Method | Path | CLI | Category |
|--------|------|-----|-------|
| GET | `/vi/health` | `sp health` | platform |
| GET | `/vi/storage/*` | `sp ops storage …` | investigation |
| GET | `/vi/schedule/monitor` | `sp ops schedule monitor` | investigation |
## Not exposed
| Method | Path | Reason |
|--------|------|--------|
| POST | `/v1/traces` | OTLP |
| POST | `/api/storage/record/save` | Agent write |
| \* | `/api/replay/local/*` | Local runner |
Controller source: `sp-tr-api/sp-web-api`, `sp-storage/sp-storage-web-api`, `sp-replay-schedule/sp-schedule-web-api`.
---
---
url: /zh/testing/agents/authentication.md
---
# Authentication
Console APIs require the HTTP header:
```http
access-token:
```
(Defined as `Constants.ACCESS_TOKEN` in sp-tr-api.)
## Non-interactive login (agents + CI)
### Email verification flow
1. Request code (human step or separate automation):
```http
GET /api/login/getVerificationCode/{userName}
```
2. Exchange code for token:
```bash
sp auth login --email user@corp.com --code 123456 --json
```
**REST:** `POST /api/login/verify` with body `{ "userName", "verifyCode", ... }`
3. CLI writes token to the shared config file in
`${XDG_CONFIG_HOME:-~/.config}/softprobe/config.jsonc` unless `--no-save`.
### CI / agent hosts
**CLI**(录制、回放、应用管理)使用你的用户 JWT:
```bash
export SP_TOKEN="eyJ..."
sp app list --json
```
**Java agent** 在 Softprobe Cloud 上使用**租户 API key**(长期有效,作用域为该组织):
```bash
export SP_TENANT_API_KEY="…" # 来自 sp tenant key ensure 或仪表盘 Settings
export SP_TENANT_ID="35"
sp agent command --app --json
```
切勿将 token 或 API key 提交到 git。泄露后立即轮换。
### Token refresh
```bash
sp auth refresh --user user@corp.com --json
```
**REST:** `GET /api/login/refresh/{userName}`
Add `--no-save` when an agent or CI job should receive a token without touching
local config:
```bash
export SP_TOKEN="$(sp auth refresh --user user@corp.com --no-save --json | jq -r .data.token)"
```
## Guest login
If enabled on the server:
```bash
sp auth login --guest --json
```
**REST:** `POST /api/login/loginAsGuest`
## OAuth
OAuth flows are browser-based. Agents should use pre-provisioned `SP_TOKEN` rather than driving OAuth interactively.
Document for humans: `GET /api/login/oauthInfo/{oauthType}`, `POST /api/login/oauthLogin`.
## Who am I
```bash
sp auth whoami --json
```
Decodes JWT `userName` or calls profile endpoint when implemented.
## Errors
| Situation | Exit code |
|-----------|-----------|
| Missing token with `--json` | `3` (`AUTH_REQUIRED`) |
| Expired or invalid token | `1` (`API_ERROR`) |
## Related
* [config](/zh/testing/installation/configuration)
* [auth command](/zh/testing/commands/auth)
---
---
url: /zh/testing/commands.md
---
# Commands
Reference for public `sp` subcommands. Use `--json` on API-backed commands. See [Output contract](/zh/testing/agents/output-contract).
## Lifecycle (recommended)
Job-oriented commands that follow record-and-replay order:
| Command | Synopsis |
|---------|----------|
| [demo](./demo) | `start`、`traffic`、`replay`、`status`、`stop` —— [Travel OTA demo](https://github.com/softprobe/demo-ota) 一体化演示栈 |
| [setup](./setup) | 配置自托管后端 URL;可选 Spcode Service(Linux) |
| [agent](./agent) | `download`, `command` — install jar and JVM flags |
| [record](./record) | `case list` — recorded entry cases before replay |
| [diagnose](./diagnose) | `replay`, `trace` — bundled investigation workflows |
## Platform
Connect, authenticate, manage apps and policies, run replay plans.
| Command | Synopsis |
|---------|----------|
| [config](./config) | Profiles, URL, init |
| [auth](./auth) | Login, whoami, refresh |
| [app](./app) | List, create, agent status, recent replays |
| [policy](./policy) | Recording, mock, compare YAML policies |
| [replay](./replay) | Run, status, stop, rerun plans |
| [health](./health) | Cluster health |
| `version` | CLI version string |
## Investigation
Recorded data, traces, and replay failures.
| Command | Synopsis |
|---------|----------|
| [record](./record) | Query recordings, completeness |
| [logs](./logs) | 按 `trace_id` 关联日志 —— 见[日志关联 ID](/zh/testing/reference/log-correlation-ids) |
| [trace](./trace) | Find traces by business attributes |
| [replay case](./replay-case) | List cases, metadata, mock tree |
| [replay diff](./replay-diff) | Diff artifacts, compare results |
| [extraction-rule](./extraction-rule) | Business attribute extraction rules |
Investigation commands support `--out-dir`, `--page`, and `--limit` unless noted.
## Administration
Groups, system config, diagnostics, legacy APIs.
| Command | Synopsis |
|---------|----------|
| [group](./group) | User groups and app grants |
| [grant](./group) | App grant listing (`grant list`) |
| [system](./system) | System config keys |
| [ops](./ops) | Storage and schedule diagnostics |
| [config legacy](./config-legacy) | Legacy `/api/config/*` (deprecated) |
## Global flags
```text
--json Machine-readable stdout (required for agents)
--profile Config profile name
--api-url Override backend URL
--token Override JWT (else SP_TOKEN / config)
--config Extra config file (JSONC overlay)
--quiet Suppress non-error stderr
--out-dir Directory for artifact files
--page Page number (investigation list commands)
--limit Page size / max items
```
## Cheat sheet
```bash
sp config init && sp auth login --email u@c.com --code 123456 --json
sp doctor --json
sp app create my-svc --json
curl -fsSL -o sp-agent.jar https://install.softprobe.ai/artifacts/agent/latest/sp-agent.jar
sp agent command --app --agent-jar ./sp-agent.jar --json # copy startCommand into your run script
sp record case list --app --since -1h --json
sp replay run --app --env http://your-service:8080 --from -24h --json
sp replay status --watch --json
sp diagnose replay --failed-only --out-dir .sp-work --json
```
## Related
* [Quickstart](/zh/testing/getting-started)
* [For AI agents](/zh/testing/agents/overview)
* [Examples](/zh/testing/examples/agent-diagnose-replay)
* [API mapping](/zh/testing/reference/api-mapping)
---
---
url: /zh/testing/agents/concepts.md
---
# Concepts
Java 录制回放产品说明(Agent、策略、回放语义)见 [Softprobe 测试](/zh/testing/)。
Istio/Envoy 网格采集与 SESSIFY 会话上下文(与 Java Agent 不同)见[平台核心概念](/zh/platform/advanced-guides/concepts)。
## Application (`appId`)
A registered service under test. Recording, replay, policies, and extraction rules are all scoped by **`appId`**.
| Field | Role |
|-------|------|
| `appName` | Unique label supplied at registration (`sp app create `). |
| `appId` | System-generated id (16-character hex). Configure the Java agent and CLI with this value. |
After registration, save `data.appId` from the create response. Attach the SoftProbe Java agent to your JVM with that id and your sp-backend URL, then confirm connectivity with `sp app status ` or `sp app list --json`.
**Agent status** (`online`, `offline`, `never`) is derived from instance heartbeats, not from the app document alone. The server marks an app `offline` when the freshest heartbeat is older than the configured threshold (default 60 seconds).
**CLI reference:** [sp app](/zh/testing/commands/app)
## Java agent
The SoftProbe Java agent is attached to the service under test with `-javaagent:/path/to/sp-agent.jar`. It operates like an observability agent operationally, but its purpose is test data capture and replay:
* During **recording**, it observes real requests and dependency interactions and uploads mocker data keyed by `appId` and trace/case ids.
* During **replay**, it restores recorded dependency behavior according to mock policy and emits replay data for comparison.
* It reports heartbeat/status so `sp app status ` can tell whether an app is `online`, `offline`, or `never`.
Minimum startup flags:
```bash
java \
-javaagent:/opt/softprobe/sp-agent.jar \
-Dsp.app.id= \
-Dsp.api.url=http://:8090 \
-jar app.jar
```
Pin `sp.app.id` for every production-like deployment. If the id changes between recording and replay, SoftProbe cannot reliably find the original cases or mock data.
## Replay target URL (`targetEnv`)
Replay does not use a symbolic environment label such as `staging` or `prod`. The schedule service field **`targetEnv`** is the **base URL of the running service** that will receive replayed HTTP traffic.
When you run `sp replay run --env `, the CLI sends that value as `targetEnv` on `POST /api/createPlan`. The schedule module parses it as a URI (`DefaultDeploymentEnvironmentProviderImpl`) and builds a `ServiceInstance` whose `url` is that string. Replay senders then issue requests to that URL (for example `DefaultHttpReplaySender` uses `instanceRunner.getUrl()`).
Requirements:
* Use a reachable base URL for the app under test, including scheme and host (and port when not default), for example `http://travel-ota:8080` or `https://order-service.internal:8443`.
* The URL must parse as a URI with a non-empty host; otherwise plan validation fails with *requested target env unable load active instance*.
* This is independent of **`SP_API_URL`** / `api_url` in CLI config, which points at sp-backend (storage, report, schedule APIs), not at the service being replayed.
Optional **`sourceEnv`** on the same request is a separate URI used only when you need a non-default source deployment; the demo stack often leaves it as `pro`.
**CLI reference:** [sp replay](/zh/testing/commands/replay) (`--env` → `targetEnv`)
## Recording policy
Declarative YAML (`kind: RecordingPolicy`) controlling what the agent records: sampling, operation include/exclude, time windows, sensitive-field scrubbing.
* Managed via `sp policy recording`
* Schema: [策略 YAML 指南](/zh/testing/policy-yaml-guide)
## Mock policy
Declarative YAML (`kind: MockPolicy`) controlling replay-time mocking: skip/force mock, tolerance, dependencies.
* `sp policy mock`
## Compare rules
Declarative YAML (`kind: CompareRulePolicy`) controlling diff behavior during replay comparison.
* `sp policy compare`
Policies merge by `metadata.priority`; global defaults ship in `sp-policy-rules` JAR resources.
## Replay plan
A batch replay job with a `planId`. Created by `sp replay run`, tracked with `sp replay status`. Replay plans consume cases already recorded by an instrumented app; a fresh app with no recorded traffic has nothing meaningful to replay.
Schedule service endpoints: `/api/createPlan`, `/api/progress`, `/api/stopPlan`.
## Trace、replay 与 plan ID {#trace-replay-and-plan-ids}
平台 ID 把录制、回放、diff 与**关联日志检索**串联起来。完整参考——每个 ID 的含义、在哪里获取、以及如何分诊统一日志——见 **[日志关联 ID](/zh/testing/reference/log-correlation-ids)**。
| ID | 含义 | 日志查询(v1) |
|----|------|----------------|
| `traceId` | 一次录制或回放请求流的 W3C trace id | **`sp logs --trace-id …`** 或 `GET /api/recorder/logs?trace_id=…` —— **唯一的 v1 键** |
| `replayId` | 一个 case 的一次回放**尝试** | 仅用于 diff/diagnose —— 查日志请从同一 case 行复制 **`traceId`** |
| `planId` | `sp replay run` 产生的回放计划容器 | case 列表 / diagnose —— 查日志用每个 case 的 **`traceId`** |
| `planItemId` | 计划内的操作级条目 | 同上 —— 不是日志查询键 |
| `diffId` | 用于深度 diff 拉取的比对结果行 | 用 `sp replay diff get` —— 不是日志查询键 |
**ID 在 CLI 输出中的位置**
| 命令 | 字段 |
|------|------|
| `sp replay run --json` | `planId` |
| `sp replay case list --plan … --json` | `replayId`、`traceId`、plan item id |
| `sp replay metadata --json` | `traceId`、关联的录制元数据 |
| `sp trace find … --json` | 解析业务属性时的 `traceId` |
| `sp diagnose replay --json` | 带 id 的失败 case,供后续跟进 |
当用户提供业务属性(orderId、caseId)而非 trace id 时,Agent 应通过 `sp trace find` 获取 `traceId`。回放失败后做日志诊断时,使用失败回放 case 中的 **`traceId`** 或 e2e **Softprobe correlation** 块(`trace_id` 字段)——而不是把 `replayId` 当作日志查询键。
## Historical coupling: schedule ↔ recording
Replay operation include/exclude lists on schedule configuration are **populated from recording policy at read time**, not stored independently on the schedule document.
Implications:
* Changing recording policy can change which operations appear in replay scope without editing schedule config.
* Diagnosis skills must not assume schedule Mongo documents are the sole source of operation filters.
See server comments on `ScheduleConfigurableHandler` in sp-tr-api.
Test cases are created only via **recording** (instrumented app traffic). Manual case authoring is not part of the CLI workflow.
## Related
* [For agents](./overview)
* [Commands](/zh/testing/commands/)
---
---
url: /en/testing/commands/demo.md
---
# demo
Run the bundled [Travel OTA](https://github.com/softprobe/demo-ota) demo locally with Docker.
## Commands
| Command | Description |
|---------|-------------|
| `sp demo start [--watch]` | Create app, apply policies, start Docker stack |
| `sp demo traffic` | Send sample booking traffic |
| `sp demo replay [--watch]` | Create replay plan against localhost OTA |
| `sp demo status` | Stack + agent + recording status |
| `sp demo stop` | Stop Docker stack |
See [Getting Started](/en/testing/getting-started) for the full walkthrough.
---
---
url: /zh/testing/commands/demo.md
---
# demo
> 本页翻译可能滞后于英文版,如有出入以[英文版](/en/testing/commands/demo)为准。
使用 Docker 在本地运行内置的 [Travel OTA](https://github.com/softprobe/demo-ota) 演示。
## Commands
| Command | Description |
|---------|-------------|
| `sp demo start [--watch]` | 创建应用、应用策略并启动 Docker 栈 |
| `sp demo traffic` | 发送示例预订流量 |
| `sp demo replay [--watch]` | 针对 localhost OTA 创建回放计划 |
| `sp demo status` | 栈、agent 及录制状态 |
| `sp demo stop` | 停止 Docker 栈 |
完整操作流程参见[快速开始](/zh/testing/getting-started)。
---
---
url: /zh/testing/installation/doctor.md
---
# Doctor
使用 `sp doctor` 检查自托管 Softprobe 安装状态:
```bash
sp doctor
sp doctor --json
```
检查项包括后端可达性和内部编码引擎安装状态。
若已通过 [`sp setup --install-spcode-service`](/zh/testing/installation/#spcode-service) 安装 **Spcode Service**(`spcode-web.service` 已注册),`sp doctor` 还会检查服务是否健康,以及能否访问已配置的 Softprobe 后端。
---
---
url: /en/testing/examples/agent-attr-trace-lookup.md
---
# Example: Business ID → trace
**Audience:** AI agents
**Goal:** User says "order ORD-1234 failed in replay" without providing `traceId`.
## Steps
```bash
sp app list --json
sp trace find \
--app my-app \
--attr-name orderId \
--attr-value ORD-1234 \
--json
```
If multiple traces:
```bash
sp trace get --json
```
Pick the trace matching time window or endpoint.
## Continue diagnosis
```bash
sp record query --trace-id --out-dir .sp-work --json
sp record completeness --json
```
If a replay already exists:
```bash
sp replay metadata --json
```
## Skill note
Do **not** ask the user to open the UI to find traceId when extraction rules index their business attributes. Prefer `sp trace find` first.
## Related
* [trace](/en/testing/commands/trace)
* [Diagnose replay failure](./agent-diagnose-replay)
---
---
url: /zh/testing/examples/agent-attr-trace-lookup.md
---
# Example: Business ID → trace
**Audience:** AI agents
**Goal:** User says "order ORD-1234 failed in replay" without providing `traceId`.
## Steps
```bash
sp app list --json
sp trace find \
--app my-app \
--attr-name orderId \
--attr-value ORD-1234 \
--json
```
If multiple traces:
```bash
sp trace get --json
```
Pick the trace matching time window or endpoint.
## Continue diagnosis
```bash
sp record query --trace-id --out-dir .sp-work --json
sp record completeness --json
```
If a replay already exists:
```bash
sp replay metadata --json
```
## Skill note
Do **not** ask the user to open the UI to find traceId when extraction rules index their business attributes. Prefer `sp trace find` first.
## Related
* [trace](/zh/testing/commands/trace)
* [Diagnose replay failure](./agent-diagnose-replay)
---
---
url: /en/testing/examples/ci-policy-gate.md
---
# Example: CI policy gate
**Audience:** GitHub Actions / GitLab CI (agents may generate this YAML)
Validate policy files on every pull request; apply only on merge to main.
## GitHub Actions
```yaml
jobs:
policy-validate:
runs-on: ubuntu-latest
env:
SP_API_URL: https://sp-staging.example.com
SP_TOKEN: ${{ secrets.SP_TOKEN }}
steps:
- uses: actions/checkout@v4
- name: Install sp
run: |
curl -fsSL -o sp "${SP_DOWNLOAD_URL:-https://install.softprobe.ai/artifacts/sp/latest}/sp-linux-amd64"
chmod +x sp && sudo mv sp /usr/local/bin/
- name: Validate recording policy
run: sp policy recording validate -f policies/recording.yaml --json
- name: Validate mock policy
run: sp policy mock validate -f policies/mock.yaml --json
```
Exit code non-zero fails the job.
## Apply on deploy (main only)
```yaml
- name: Apply policies
if: github.ref == 'refs/heads/main'
run: |
sp policy recording apply -f policies/recording.yaml --json
sp policy mock apply -f policies/mock.yaml --json
```
## Agent-generated pipelines
When an agent edits policy YAML in a repo, it should:
1. Run `validate` locally against staging `SP_API_URL`
2. Commit YAML + open PR
3. Not embed `SP_TOKEN` in repo files
## Related
* [policy](/en/testing/commands/policy)
* [GitOps policies](./gitops-policies)
---
---
url: /zh/testing/examples/ci-policy-gate.md
---
# Example: CI policy gate
**Audience:** GitHub Actions / GitLab CI (agents may generate this YAML)
Validate policy files on every pull request; apply only on merge to main.
## GitHub Actions
```yaml
jobs:
policy-validate:
runs-on: ubuntu-latest
env:
SP_API_URL: https://sp-staging.example.com
SP_TOKEN: ${{ secrets.SP_TOKEN }}
steps:
- uses: actions/checkout@v4
- name: Install sp
run: |
curl -fsSL -o sp "${SP_DOWNLOAD_URL:-https://install.softprobe.ai/artifacts/sp/latest}/sp-linux-amd64"
chmod +x sp && sudo mv sp /usr/local/bin/
- name: Validate recording policy
run: sp policy recording validate -f policies/recording.yaml --json
- name: Validate mock policy
run: sp policy mock validate -f policies/mock.yaml --json
```
Exit code non-zero fails the job.
## Apply on deploy (main only)
```yaml
- name: Apply policies
if: github.ref == 'refs/heads/main'
run: |
sp policy recording apply -f policies/recording.yaml --json
sp policy mock apply -f policies/mock.yaml --json
```
## Agent-generated pipelines
When an agent edits policy YAML in a repo, it should:
1. Run `validate` locally against staging `SP_API_URL`
2. Commit YAML + open PR
3. Not embed `SP_TOKEN` in repo files
## Related
* [policy](/zh/testing/commands/policy)
* [GitOps policies](./gitops-policies)
---
---
url: /en/testing/examples/agent-diagnose-replay.md
---
# Example: Diagnose a failed replay
**Audience:** AI agent skill (OpenCode, Claude Code, Codex)
**Goal:** Given a failing replay plan, find failed cases, fetch diff artifact, triage unified logs by `trace_id`.
## Prerequisites
```bash
export SP_API_URL=http://127.0.0.1:8090
export SP_TOKEN=
```
## One-shot (preferred)
```bash
sp diagnose replay plan-xyz --failed-only --out-dir .sp-work --json
```
Read `data.artifacts[]` paths locally. See [diagnose](/en/testing/commands/diagnose).
## Manual steps
### 1. Confirm backend and list apps
```bash
sp health --json
sp app list --json
```
Select `appId` from `data.items[].appId`.
### 2. Find the plan
If the user supplied `planId`, skip. Otherwise:
```bash
sp app replays my-app --limit 5 --json
```
### 3. List failed cases
```bash
sp replay case list --plan plan-xyz --failed --page 1 --limit 20 --json
```
From each item collect `diffId`, `replayId`, `planItemId`, and **`traceId`** (log lookup key).
### 4. Fetch diff (artifact)
```bash
sp replay diff get diff-abc --out-dir .sp-work --json
```
Parse `data.artifact` and read the JSON file in a follow-up tool call. Inspect `baseMsg` vs `testMsg`.
### 5. Optional: correlated runtime logs
Pull agent, application, and sp-backend logs for a failed case with `sp logs`, keyed by the failed case's `traceId`. Start with **backend replay send markers** — they show whether schedule reached your app:
```bash
sp logs --trace-id --since --until --json
```
Filter `backend` rows for `Replay send start`, `Replay send done`, and `Replay send failed`. See [Replay send log markers](/en/testing/reference/replay-send-log-markers).
When failure may be recording-time agent behavior:
```bash
TRACE_ID=""
SINCE="2026-06-27T10:00:00Z"
UNTIL="2026-06-27T10:05:00Z"
curl -s "${SP_API_URL}/api/recorder/logs?trace_id=${TRACE_ID}&since=${SINCE}&until=${UNTIL}" \
-H "Accept: application/json" -o .sp-work/unified-logs.json
jq '.rows | length' .sp-work/unified-logs.json
jq '[.rows[].source] | group_by(.) | map({source: .[0], n: length})' .sp-work/unified-logs.json
jq '.warnings' .sp-work/unified-logs.json
jq -r '.rows[] | select(.source=="backend" and .severity=="ERROR") | .body' .sp-work/unified-logs.json | head -20
```
The `curl` above hits the unified-log API directly; the `sp logs` CLI wraps the same endpoint:
```bash
sp logs --trace-id "$TRACE_ID" --since "$SINCE" --until "$UNTIL" --json > .sp-work/unified-logs.json
```
**Triage:** empty rows + `warnings` → backend/schema skew; empty + no warnings → wrong window or ingest lag; all three `source` values → pipeline OK, focus on diff + log bodies.
See [Log correlation IDs](/en/testing/reference/log-correlation-ids) and [sp logs](/en/testing/commands/logs).
Do **not** use `sp recorder logs --replay-id`, `sp record logs`, `sp replay logs`, or legacy `/api/record-logs/*` / `/api/replay-logs/*`.
### 6. Optional: metadata for full-link
```bash
sp replay metadata --json
```
Use `data.fullLink` to decide if `sp replay compare` needs `traceId`.
## Skill prompt snippet
```markdown
When diagnosing SoftProbe replay failures:
1. Run `sp replay case list --plan --failed --json`
2. For each diffId: `sp replay diff get --out-dir .sp-work --json`
3. Read the artifact file path from JSON; do not parse multi-MB stdout
4. For logs: copy `traceId` from the failed case; curl `GET /api/recorder/logs?trace_id=…&since=…&until=…`; jq row count and group by `source`; read backend then agent then app ERROR lines
5. On pytest failure: read Softprobe correlation (`trace_id`) and Unified logs summary in output
6. If traceId unknown, ask user for business ID and run `sp trace find`
```
## Related
* [Replay send log markers](/en/testing/reference/replay-send-log-markers)
* [replay-diff](/en/testing/commands/replay-diff)
* [sp logs](/en/testing/commands/logs)
* [Log correlation IDs](/en/testing/reference/log-correlation-ids)
---
---
url: /zh/testing/examples/agent-diagnose-replay.md
---
# Example: Diagnose a failed replay
**Audience:** AI agent skill (OpenCode, Claude Code, Codex)
**Goal:** Given a failing replay plan, find failed cases, fetch diff artifact, optionally inspect record logs.
## Prerequisites
```bash
export SP_API_URL=http://127.0.0.1:8090
export SP_TOKEN=
```
## One-shot (preferred)
```bash
sp diagnose replay plan-xyz --failed-only --out-dir .sp-work --json
```
Read `data.artifacts[]` paths locally. See [diagnose](/zh/testing/commands/diagnose).
## Manual steps
### 1. Confirm backend and list apps
```bash
sp health --json
sp app list --json
```
Select `appId` from `data.items[].appId`.
### 2. Find the plan
If the user supplied `planId`, skip. Otherwise:
```bash
sp app replays my-app --limit 5 --json
```
### 3. List failed cases
```bash
sp replay case list --plan plan-xyz --failed --page 1 --limit 20 --json
```
From each item collect `diffId`, `replayId`, `planItemId`, `traceId`.
### 4. Fetch diff (artifact)
```bash
sp replay diff get diff-abc --out-dir .sp-work --json
```
Parse `data.artifact` and read the JSON file in a follow-up tool call. Inspect `baseMsg` vs `testMsg`.
### 5. Optional: correlated runtime logs
When failure may be agent-side (no recording, incomplete trace), pull the unified logs for the failed case's `traceId`:
```bash
sp logs --trace-id --since --until --json
```
Filter `backend` rows for `Replay send start` / `done` / `failed`. See [Replay send log markers](/zh/testing/reference/replay-send-log-markers).
### 6. Optional: metadata for full-link
```bash
sp replay metadata --json
```
Use `data.fullLink` to decide if `sp replay compare` needs `traceId`.
## Skill prompt snippet
```markdown
When diagnosing SoftProbe replay failures:
1. Run `sp replay case list --plan --failed --json`
2. For each diffId: `sp replay diff get --out-dir .sp-work --json`
3. Read the artifact file path from JSON; do not parse multi-MB stdout
4. If traceId unknown, ask user for business ID and run `sp trace find`
```
## Related
* [replay-diff](/zh/testing/commands/replay-diff)
---
---
url: /en/testing/examples/gitops-policies.md
---
# Example: GitOps policies
**Audience:** Platform engineers and agents maintaining policy repos
## Repository layout
```text
policies/
├── recording-staging.yaml
├── recording-prod.yaml
├── mock-staging.yaml
└── compare-global.yaml
```
## Export from server (bootstrap)
```bash
sp policy recording list --json
sp policy recording export -o policies/recording-prod.yaml
```
## Drift check (agent or CI)
```bash
sp policy recording validate -f policies/recording-prod.yaml --json
# optional v2:
# sp policy recording diff -f policies/recording-prod.yaml --against prod-id
```
## Apply after review
```bash
sp policy recording apply -f policies/recording-prod.yaml --json
```
## Multi-environment profiles
```jsonc
// ~/.config/softprobe/config.jsonc
{
"profiles": {
"staging": {
"api_url": "https://sp-staging.example.com"
},
"prod": {
"api_url": "https://sp.example.com"
}
}
}
```
```bash
sp --profile staging policy recording apply -f policies/recording-staging.yaml --json
sp --profile prod policy recording apply -f policies/recording-prod.yaml --json
```
## Related
* [Policy YAML](/en/testing/policies)
* [CI policy gate](./ci-policy-gate)
---
---
url: /zh/testing/examples/gitops-policies.md
---
# Example: GitOps policies
**Audience:** Platform engineers and agents maintaining policy repos
## Repository layout
```text
policies/
├── recording-staging.yaml
├── recording-prod.yaml
├── mock-staging.yaml
└── compare-global.yaml
```
## Export from server (bootstrap)
```bash
sp policy recording list --json
sp policy recording export -o policies/recording-prod.yaml
```
## Drift check (agent or CI)
```bash
sp policy recording validate -f policies/recording-prod.yaml --json
# optional v2:
# sp policy recording diff -f policies/recording-prod.yaml --against prod-id
```
## Apply after review
```bash
sp policy recording apply -f policies/recording-prod.yaml --json
```
## Multi-environment profiles
```jsonc
// ~/.config/softprobe/config.jsonc
{
"profiles": {
"staging": {
"api_url": "https://sp-staging.example.com"
},
"prod": {
"api_url": "https://sp.example.com"
}
}
}
```
```bash
sp --profile staging policy recording apply -f policies/recording-staging.yaml --json
sp --profile prod policy recording apply -f policies/recording-prod.yaml --json
```
## Related
* [Policy YAML](/zh/testing/policies)
* [CI policy gate](./ci-policy-gate)
---
---
url: /zh/testing/reference/exit-codes.md
---
# Exit codes
Contract for agents and CI scripts invoking `sp`.
| Code | Name | Meaning |
|------|------|---------|
| `0` | Success | Parsed JSON on stdout when `--json` |
| `1` | API\_ERROR | Backend returned error or HTTP non-success (includes `NO_RECORDED_CASES` on `replay run`) |
| `2` | USAGE / PROFILE\_NOT\_FOUND / CONFIG\_\* | Invalid flags, missing config, parse errors, unknown profile |
| `3` | AUTH\_REQUIRED | No token available in non-interactive mode |
## stderr with `--json`
On exit `1` or `2` or `3`, stderr contains a JSON error object:
```json
{
"ok": false,
"command": "app list",
"error": {
"code": "AUTH_REQUIRED",
"message": "Set SP_TOKEN or run sp auth login"
}
}
```
## Config error codes (exit `2`)
| Code | Cause |
|------|-------|
| `CONFIG_MISSING` | No `config.jsonc` / `sp.jsonc`; run `sp config init` |
| `CONFIG_PARSE_ERROR` | Invalid JSONC or schema validation failure |
| `CONFIG_WRITE_ERROR` | Could not write config file |
| `PROFILE_NOT_FOUND` | Selected profile missing from merged config |
## Agent retry guidance
| Code | Retry? |
|------|--------|
| 0 | No |
| 1 | Sometimes — after fixing data or if transient 5xx |
| 2 | No — fix invocation |
| 3 | After refresh/login |
---
---
url: /zh/testing/agents/overview.md
---
# For AI agents
This page is the primary entry point for authors of **OpenCode / spcode**, **Claude Code**, **Codex**, **Cursor**, and other agent hosts that invoke SoftProbe through shell tools.
## 把文档喂给你的 Agent
整套文档以 [llmstxt.org](https://llmstxt.org/) 格式发布,Agent 无需抓取 HTML 即可直接消费:
| URL | 内容 |
|-----|------|
| [`/llms.txt`](/llms.txt) | 带描述的全站页面索引 —— 体积小的入口 |
| [`/llms-full.txt`](/llms-full.txt) | 整套文档正文合并为单个纯文本文件 |
| `‹任意页面›.md` | 单个页面的 Markdown 源(在 URL 后追加 `.md`) |
先让 Agent 读 `/llms.txt`,它会按需跟进到全文或单页 `.md`。
## Design goals
1. **One process, one job** — each tool call runs a single `sp` command with explicit flags. Do not rely on shell aliases or interactive prompts.
2. **Always pass `--json`** for machine parsing unless you are showing output to a human in chat.
3. **Use artifacts for large payloads** — diff bodies, log downloads, and record queries write files under `--out-dir`; stdout returns paths and summaries only.
4. **Backend data only** — use API commands (`sp app`, `sp replay`, `sp record`, …) for cluster state.
## Recommended tool shape
Wrap `sp` as a shell tool with a fixed argv prefix:
```bash
sp --json --profile "${SP_PROFILE:-default}" ...
```
Environment variables agents should set in the host config:
| Variable | Purpose |
|----------|---------|
| `SP_API_URL` | Backend base URL (e.g. `http://127.0.0.1:8090`) |
| `SP_TOKEN` | JWT from `sp auth login` or CI secret |
| `SP_PROFILE` | Named profile in `${XDG_CONFIG_HOME}/softprobe/config.jsonc` or `sp.jsonc` |
| `SP_CONFIG` | Extra config file path loaded after `sp.jsonc` and before `--config` |
`sp` also reads `${XDG_CONFIG_HOME:-~/.config}/softprobe/config.jsonc` for
shared SoftProbe settings and `${XDG_CONFIG_HOME:-~/.config}/softprobe/sp.jsonc`
for CLI-specific overrides. Agents should prefer `SP_API_URL` and `SP_TOKEN`
when running in ephemeral CI containers.
## Lifecycle command order
Use these job-oriented commands before low-level building blocks:
1. `sp doctor --json`
2. `sp app create --json` → save `data.appId`
3. `sp policy recording apply -f … --json`
4. 从 `install.softprobe.ai` 下载 `sp-agent.jar`,然后运行 `sp agent command --app --agent-jar ./sp-agent.jar --json`
5. Start the app with `data.startCommand`; send traffic
6. `sp record case list --app --since -1h --json`
7. `sp policy mock apply` / `sp policy compare apply`
8. `sp replay run --app --env --json`
9. `sp replay status --json` or `sp diagnose replay --json`
## Typical skill flows
### Diagnose a failed replay
```mermaid
sequenceDiagram
participant Agent
participant SP as sp CLI
participant API as sp-backend
Agent->>SP: app list --json
SP->>API: GET /api/applications/list
Agent->>SP: replay case list --plan X --failed --json
SP->>API: report/storage APIs
Agent->>SP: replay diff get diffId --out-dir .sp-work --json
SP->>API: GET /api/report/queryDiffMsgById/{id}
Agent->>Agent: Read artifact file locally
```
Commands: see [Diagnose replay failure](/zh/testing/examples/agent-diagnose-replay).
### Change policy and re-run
1. `sp policy recording validate -f policy.yaml --json`
2. `sp policy recording apply -f policy.yaml --json`
3. `sp replay run --app … --env --json`
4. `sp replay status --watch --json`
## Migrating from `sp_api` (spcode plugin)
The OpenCode `@spcode/plugin` tool `sp_api` uses named endpoints (`diff_detail`, `query_replay_case`, …). The `sp` CLI exposes the same operations as **stable subcommands**.
| Old | New |
|-----|-----|
| `sp_api endpoint=list_applications` | `sp app list --json` |
| `sp_api endpoint=diff_detail diffId=…` | `sp replay diff get … --out-dir … --json` |
| `sp_api endpoint=find_traces_by_attr …` | `sp trace find … --json` |
Skills should be updated to call `sp` so behavior is consistent outside OpenCode.
## Error handling for agents
| Exit code | Meaning | Agent action |
|-----------|---------|--------------|
| `0` | Success | Parse stdout JSON |
| `1` | API or business error | Read stderr JSON `error` field; may retry or ask user |
| `2` | Usage / missing config / auth | Fix argv or run `sp auth login` / set `SP_TOKEN` |
| `3` | Auth required | Refresh token |
See [Output contract](./output-contract) and [Exit codes](/zh/testing/reference/exit-codes).
## What not to call
* Agent write APIs (`POST /api/storage/record/save`, batch saves) — instrumentation only
* OTLP ingestion (`/v1/traces`) — use collectors
* Mutating `sp ops` without `--confirm` — blocked by design
## Related pages
* [Output contract](./output-contract)
* [Versioning](./versioning)
* [Commands](/zh/testing/commands/)
---
---
url: /zh/platform/deployment/GKE-Autopilot-Istio-Installation-Guide.md
---
# GKE Autopilot + Istio 安装指南
本文介绍在 **Google Kubernetes Engine (GKE) Autopilot** 模式下安装 **Istio**,并部署 **Softprobe** 的完整流程及注意事项。
## 前提条件
* 已开通 GCP 账户并创建 GKE Autopilot 集群
* 已安装 `gcloud` 与 `kubectl`
* 拥有集群管理员权限
## 步骤概览
1. 安装 Istio
2. 启用自动 Sidecar 注入
3. 部署 Softprobe 组件
4. 验证与排错
## 1. 安装 Istio
使用官方安装方式或 Operator 管理:
* 参见 [Istio 官方安装文档](https://istio.io/latest/docs/setup/install/)
## 2. 启用自动 Sidecar 注入
在目标命名空间配置标签:
```bash
kubectl label namespace istio-injection=enabled
```
## 3. 部署 Softprobe
在启用 Sidecar 的命名空间部署 Softprobe 相关组件,并配置公钥认证与采样策略。
## 4. 验证
* 访问服务并观察请求链路
* 在 Softprobe 仪表盘查看服务依赖图与追踪样例
* 如无数据,检查 Sidecar 注入、WASM 插件与网格流量策略
## 常见问题
* Autopilot 下资源配额限制较严格,建议按需调优采样与队列
* 需确保 Istio 版本与 SP-Istio Agent 兼容
***
更多部署细节:
* 若你未使用 GKE,请参考 [生产安装指南](./installation)
## 在 Autopilot 环境进行应用程序插桩(不使用 Operator)
在部分 GKE Autopilot 场景下,集群级 Operator(如 OpenTelemetry Operator)可能受安全策略、私有集群防火墙或 Webhook 要求限制。若无法(或不希望)使用 Operator,可在应用侧手动挂载/启用各语言的 OpenTelemetry Agent,并将数据导出到 OTLP 端点(Collector 或 Softprobe 接入端点)。
### 通用配置
* 选择 OTLP 端点(Collector 服务或外部接入地址)
* 通过环境变量设置服务元数据(service.name、namespace、version 等)
* 确保工作负载到 OTLP 端点的 egress(HTTP 或 gRPC)连通性
* 遵循 Autopilot 规范:尽量使用非 root 容器,并为所有容器设置合理的资源请求/限制
常用环境变量(按需替换为你的端点):
```bash
# 禁用导出(仅采集,不上报)
export OTEL_TRACES_EXPORTER=none
export OTEL_METRICS_EXPORTER=none
export OTEL_LOGS_EXPORTER=none
# 如需开启上报,请改为:
# export OTEL_TRACES_EXPORTER=otlp
# export OTEL_METRICS_EXPORTER=otlp
# export OTEL_LOGS_EXPORTER=otlp
# 并配置 OTLP 端点与协议:
# export OTEL_EXPORTER_OTLP_ENDPOINT="https://otel.example.com"
# export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf" # 或 "grpc"
# export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer YOUR_TOKEN"
# export OTEL_SERVICE_NAME="your-service"
# export OTEL_RESOURCE_ATTRIBUTES="service.namespace=production,service.version=1.0.0"
```
可以在 Kubernetes Deployment 的 `env:` 中直接设置以上变量。
***
### Java(JVM)
通过 `-javaagent` 挂载 OpenTelemetry Java Agent 并设置环境变量:
```dockerfile
# 建议将 Agent 直接打包进镜像
ADD opentelemetry-javaagent.jar /otel/javaagent.jar
```
```yaml
# Deployment 片段(默认仅采集,不上报)
spec:
template:
spec:
containers:
- name: app
image: your-registry/your-java-app:latest
env:
- name: OTEL_TRACES_EXPORTER
value: "none"
- name: OTEL_METRICS_EXPORTER
value: "none"
- name: OTEL_LOGS_EXPORTER
value: "none"
- name: OTEL_RESOURCE_ATTRIBUTES
value: "service.namespace=production,service.version=1.0.0"
- name: OTEL_INSTRUMENTATION_HTTP_SERVER_CAPTURE_REQUEST_HEADERS
value: "x-request-id,authorization"
- name: OTEL_INSTRUMENTATION_HTTP_SERVER_CAPTURE_RESPONSE_HEADERS
value: "content-type,content-length"
- name: JAVA_TOOL_OPTIONS
value: "-javaagent:/otel/javaagent.jar"
# 如需开启上报,请将上面 three exporter 改为 otlp,并补充以下 OTLP 配置:
# - name: OTEL_EXPORTER_OTLP_ENDPOINT
# value: "https://otel.example.com"
# - name: OTEL_EXPORTER_OTLP_PROTOCOL
# value: "http/protobuf" # 或 "grpc"
# - name: OTEL_EXPORTER_OTLP_HEADERS
# value: "Authorization=Bearer YOUR_TOKEN"
# - name: OTEL_SERVICE_NAME
# value: "your-service"
```
***
### Node.js
使用 NodeSDK + auto-instrumentations:
```bash
npm install @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node @opentelemetry/exporter-trace-otlp-http
```
创建启动前置文件(例如 `otel.js`):
```js
// otel.js
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
const exporter = new OTLPTraceExporter({
// 对 HTTP 导出器,URL 会自动追加 /v1/traces
url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
headers: process.env.OTEL_EXPORTER_OTLP_HEADERS
? Object.fromEntries(process.env.OTEL_EXPORTER_OTLP_HEADERS.split(',').map(h => h.split('=')))
: undefined,
});
const sdk = new NodeSDK({
traceExporter: exporter,
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
```
以预加载方式启动:
```bash
# 方式一:require 启动前置
node -r ./otel.js app.js
# 方式二:通过 NODE_OPTIONS
export NODE_OPTIONS="--require ./otel.js" && node app.js
```
将通用环境变量配置到 Deployment 即可。
***
### Python
使用 Python Distro 与 CLI 插桩:
```bash
pip install opentelemetry-distro opentelemetry-exporter-otlp
opentelemetry-bootstrap --action=install
```
以 CLI 插桩运行:
```bash
# 先按“通用配置”设置环境变量,然后:
opentelemetry-instrument python app.py
```
也可以在代码中直接初始化 SDK 并配置 OTLP 导出。
***
### .NET
可以使用 SDK 方式进行插桩(更易落地),或启用自动插桩(Native Profiler)。下面示例为 SDK 方式:
```csharp
// Program.cs(示例)
using OpenTelemetry;
using OpenTelemetry.Trace;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenTelemetry().WithTracing(tracerProviderBuilder =>
{
tracerProviderBuilder
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddOtlpExporter(options =>
{
options.Endpoint = new Uri(Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "https://otel.example.com");
// 若使用 HTTP/protobuf,请确保协议匹配;需要时设置 headers
});
});
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
```
如需 .NET 自动插桩,请挂载自动插桩文件并在 Deployment 中设置相关 Profiler 环境变量(`CORECLR_ENABLE_PROFILING`、`CORECLR_PROFILER`、`CORECLR_PROFILER_PATH` 以及对应的 `OTEL_*` 变量)。
***
### Go
Go 通常在代码中使用 SDK 初始化:
```go
// 示意代码
import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/sdk/resource"
"go.opentelemetry.io/otel/sdk/trace"
)
func initTracer() (*trace.TracerProvider, error) {
exporter, err := otlptracehttp.New(context.Background(), otlptracehttp.WithEndpoint(os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")))
if err != nil { return nil, err }
tp := trace.NewTracerProvider(
trace.WithBatcher(exporter),
trace.WithResource(resource.Default()),
)
otel.SetTracerProvider(tp)
return tp, nil
}
```
若需要无代码方式采集 HTTP(eBPF),可考虑单独的 DaemonSet(如 Beyla)。在 Autopilot 下需确保其符合非特权限制。
***
### Autopilot 场景排错建议
* 验证到 OTLP 端点的 egress 连通性以及 TLS/证书要求
* 为所有容器定义资源请求/限制(resource requests/limits)
* 避免使用特权标志与仅 root 可写路径
* 若策略限制 initContainer,尽量将 Agent 直接打包进镜像
* 同时查看应用与后端/Collector 的日志,确认是否成功导出
### 禁用导出(仅采集,不上报)
在某些场景(例如 Autopilot 冒烟测试)中,可以保留插桩但暂时关闭导出。这样会创建本地的 span/metric/log,但不会向后端发送。
* 跨语言(环境变量):
```bash
export OTEL_TRACES_EXPORTER=none
export OTEL_METRICS_EXPORTER=none
export OTEL_LOGS_EXPORTER=none
```
如上将禁用所有导出器,插桩仍会创建数据,但不会对外发送。
* Java(OpenTelemetry Java Agent)示例:
```bash
JAVA_TOOL_OPTIONS="-javaagent:/otel/opentelemetry-javaagent.jar \
-Dotel.traces.exporter=none \
-Dotel.metrics.exporter=none \
-Dotel.logs.exporter=none \
-Dotel.resource.attributes=service.name=sp-storage \
-Dotel.instrumentation.http.server.capture-request-headers=tracestate \
-Dotel.instrumentation.http.server.capture-response-headers=tracestate"
```
或直接在 JVM 启动命令追加系统属性:
```bash
java -javaagent:/otel/opentelemetry-javaagent.jar \
-Dotel.traces.exporter=none \
-Dotel.metrics.exporter=none \
-Dotel.logs.exporter=none \
-Dotel.resource.attributes=service.name=sp-storage \
-Dotel.instrumentation.http.server.capture-request-headers=tracestate \
-Dotel.instrumentation.http.server.capture-response-headers=tracestate \
-jar app.jar
```
注意:
* 关闭导出后可减少外部流量,便于验证;但插桩的开销仍然存在(会创建数据)。生产环境请恢复所需导出(例如设置 `OTEL_TRACES_EXPORTER=otlp`)。
* 建议始终配置好服务资源属性(service.name、namespace、version 等),以便后续随时开启导出,无需改动应用清单。
---
---
url: /zh/testing/agents/introduction.md
---
# Introduction
SoftProbe is a record-and-replay testing system for Java services. It attaches to an application as a `-javaagent`, observes real traffic in the background, records request and dependency data, then replays recorded cases later with automatic mocking and automatic comparison.
The SoftProbe CLI (`sp`) is the command-line interface to the SoftProbe backend (**sp-backend**). It is specified **AI-agent first**: stable JSON output, predictable exit codes, and artifact files for large diagnosis payloads.
## How SoftProbe Works
SoftProbe has four moving parts:
| Part | Role |
|------|------|
| **Instrumented application** | Your Java service, started with `-javaagent:/path/to/sp-agent.jar`. |
| **SoftProbe Java agent** | Weaves bytecode at runtime, similar operationally to an OpenTelemetry Java agent. It records HTTP, DB, cache, RPC, config, auth, dynamic class, and other dependency data without application code changes. |
| **SoftProbe backend** | Stores recorded cases, policies, replay plans, replay logs, diff results, extraction rules, app config, and agent heartbeats. |
| **`sp` CLI** | Registers apps, configures policies/rules, checks agent status, queries recorded data, starts replay plans, and diagnoses replay failures. |
The normal lifecycle is:
1. **Setup** the CLI and confirm the backend is reachable.
2. **Create an app** and get an `appId`.
3. **List the app** and confirm it is visible to the current user or agent token.
4. **Set recording policy** so the agent knows what traffic to capture.
5. **Install the Java agent** (`sp-agent.jar`) on the service host or image.
6. **Run the app with the agent in recording mode**, pinning `sp.app.id` to the registered `appId`, then send real or test traffic through the app.
7. **List recorded cases** and verify the expected requests were captured.
8. **Set replay policy**: mock policy and compare policy decide what is mocked and how differences are judged.
9. **Create a replay plan** from the recorded cases and run replay.
10. **Check replay results** with case, diff, log, trace, and mock-tree commands.
Replay is therefore not the first action in a fresh system. A replay plan requires recorded cases from an app that was actually run with the SoftProbe agent.
## Agent Startup Example
The agent is attached with a JVM `-javaagent` flag. Pin the app id explicitly in production; do not rely on inferred service names.
```bash
java \
-javaagent:/opt/softprobe/sp-agent.jar \
-Dsp.app.id=a1b2c3d4e5f67890 \
-Dsp.api.url=http://127.0.0.1:8090 \
-jar order-service.jar
```
`sp.app.id` is the identity SoftProbe uses to isolate recordings, pull config, and match replay data. It is separate from `OTEL_SERVICE_NAME`, which only describes telemetry resource attributes.
## Single public surface
All documented `sp` commands call **sp-backend** over HTTP (`:8090` by default). The Java agent also talks to backend services to pull config, send heartbeats, and upload recording/replay observability data.
## Who uses `sp`?
1. **AI coding agents** — replay failure diagnosis, trace lookup by business ID, policy validation, automated re-runs
2. **CI/CD** — policy gates on pull requests, scheduled replay smoke tests
3. **Platform engineers** — same commands as agents, often without `--json`
The web UI remains available for visual inspection and manual review; the CLI covers **scriptable** and **agent** workflows focused on record-and-replay.
## Relationship to other tools
| Tool | Role |
|------|------|
| **Web UI** | Visual diff, trace inspection, and console review |
| **Java agent** | In-process instrumentation, recording, replay-time mocking, and replay observability |
| **`spcode` CLI** | Local autonomous AI engine and interactive terminal workspace. Shared SSO configuration. |
| **[策略 YAML 指南](/zh/testing/policy-yaml-guide)** | `sp policy` 输入的 schema 与示例 |
## Documentation map
* [Softprobe Testing](/zh/testing/) — product overview (Java agent, record/replay, policies)
* [For AI agents](./overview) — start here if you write skills or tools
* [spcode CLI](/zh/testing/installation/code) — local autonomous AI engine and interactive terminal workspace
* [Commands](/zh/testing/commands/) — platform, investigation, and administration
---
---
url: /zh/platform/deployment/installation.md
---
# Istio WASM 插件安装
将 SP-Istio Agent 部署到您的生产 Istio 服务网格中。
::: tip 查找用户安装?
Softprobe 服务端安装(Helm sp-backend)、客户端安装(`sp setup`、Spcode Service)、`sp code`、`sp doctor` 与 `sp upgrade` 位于 [测试安装](/zh/testing/installation/)。本平台页面仅面向 Istio 网格 / SESSIFY 部署。
:::
::: tip 下一步:在 Dashboard 查看 Context View
部署 SP‑Istio Agent 并初始化 SESSIFY 后,先在应用中产生一些流量,然后:
1. 打开 Softprobe 仪表盘 → Context View
2. 选择与部署相匹配的时间范围与环境(env)
3. 如有需要可按 serviceName 过滤;也可用 userId/sessionId/request\_body\_hash 搜索定位会话
4. 点击会话查看端到端图谱、span、客户端指标与交互事件
无需改动服务端代码即可获得完整上下文可视化。
:::
## 先决条件
在生产环境中安装 SP-Istio Agent 之前,请确保您拥有:
* 一个正在运行的 Kubernetes 集群
* 已安装并配置好 Istio
* 具有适当权限的 kubectl 访问权限
* 到 Softprobe 端点的网络连接
## 安装
使用您在 [账户设置](../getting-started/account-setup) 阶段下载的个性化 `minimal.yaml` 文件安装 SP-Istio Agent。此文件包含您唯一的 API 密钥和预配置设置。
```bash
# 确保您使用的是从 Softprobe 控制台下载的 minimal.yaml 文件
kubectl apply -f minimal.yaml
```
这将在您的 Istio 服务网格中全局部署 WasmPlugin。
::: tip 前端 SESSIFY(可选但推荐)
若要在浏览器侧采集性能与用户行为数据,请在应用前端安装 Softprobe SESSIFY。详见 [前端插件安装](/zh/platform/sessify)。
:::
## 验证安装
检查 WasmPlugin 是否已成功创建:
```bash
kubectl get wasmplugin -A
```
您应该会看到列出的 SP-Istio Agent 插件。
## 配置
默认配置会捕获网格中所有服务的 HTTP 流量。您可以通过修改 WasmPlugin 资源来自定义行为。
## 前端可观测性与会话关联(sessionId)
若要实现从浏览器到后端的全链路追踪,请在应用前端集成 Softprobe SESSIFY。SDK 会在每个浏览器标签页初始化时自动生成并复用唯一的 sessionId:
* 同一标签页内的页面跳转与交互会复用同一个 sessionId,使一次用户访问能够完整串联。
* 打开新的标签页或窗口会生成新的 sessionId;关闭标签页或重新初始化后,会话也会随之重置。
* 所有上报的前端事件、性能指标与网络请求都会携带该 sessionId,便于与后端链路数据进行关联,形成端到端视图。
安装并在应用入口初始化:
```bash
npm install @softprobe/sessify
```
```jsx
// app/layout.tsx 或你的应用入口文件
'use client'
import { useEffect } from 'react';
import { initSessify } from '@softprobe/sessify';
export default function RootLayout({ children }) {
useEffect(() => {
// 使用默认配置初始化会话管理
initSessify({});
}, []);
return (
{children}
);
}
```
框架专属示例(React/Vue/Next.js)与进阶用法见完整的 [SESSIFY 集成](/zh/platform/sessify)。
最佳实践:
* 在前端将 sessionId 通过请求头(例如 `X-Session-Id`)或 Tracing Context 传递到后端;
* 在后端采集与日志中记录该标识,或在可观测性系统中进行关联,以提升排障与定位效率。
参考文档:
* [前端插件安装](/zh/platform/sessify)
* [配置指南](/zh/platform/configuration/config)
### 范围化部署
要仅将代理部署到特定的命名空间或工作负载,您可以创建一个范围化的 WasmPlugin 配置。请参阅 [配置指南](/zh/platform/configuration/config) 以获取详细的配置选项。
## 使用 Bookinfo 演示进行测试
要使用 Istio 的 Bookinfo 演示应用程序验证安装:
```bash
# 为默认命名空间启用 Istio 注入
kubectl label namespace default istio-injection=enabled --overwrite
# 部署 Bookinfo 应用程序
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.22/samples/bookinfo/platform/kube/bookinfo.yaml
# 部署 Bookinfo 网关
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.22/samples/bookinfo/networking/bookinfo-gateway.yaml
# 应用范围化的测试配置
kubectl apply -f https://raw.githubusercontent.com/softprobe/softprobe/main/deploy/test-bookinfo.yaml
```
### 生成测试流量
```bash
# 获取入口网关 URL
export GATEWAY_URL=$(kubectl -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
# 生成一些流量
curl -sf "http://${GATEWAY_URL}/productpage" >/dev/null
# 验证插件是否正常工作
kubectl get wasmplugin -A
```
## 卸载
要从您的集群中移除 SP-Istio Agent:
```bash
kubectl delete wasmplugin -n istio-system sp-istio-agent
```
---
---
url: /zh/testing/java-agent.md
---
# Softprobe Java Agent
Softprobe Java Agent(`sp-agent.jar`)通过 `-javaagent` 挂载到 JVM。它在字节码层织入各类框架(*部署方式*上类似 OpenTelemetry Java Agent),但目的是**测试数据采集与回放时 Mock**,而非通用分布式追踪。
::: warning 不是 Istio/Envoy Agent
网格采集见[平台 Agent 架构](/zh/platform/advanced-guides/agent-architecture)。本节仅介绍 **JVM** Agent。
:::
## 前置条件
* 可通过 JVM 参数重启的 Java 服务
* Agent 主机可访问 **sp-backend**(本地默认 `http://127.0.0.1:8090`)
* 已注册 **`appId`** — 使用 `sp app create` 创建,并在所有实例上固定同一 id
## 启动命令
使用 `-javaagent` 及下列 JVM 参数挂载 Agent:
```bash
java \
-javaagent:sp-agent.jar \
-Dsp.app.id= \
-Dsp.api.url=http://127.0.0.1:8090 \
-jar your-service.jar
```
| 参数 | 指向 | 含义 |
|------|------|------|
| `-Dsp.app.id` | — | 注册应用 id(`sp app create` 返回的 16 位十六进制)。**请在共享录制的各环境固定此值。** |
| `-Dsp.api.url` | **sp-backend**(如 `:8090`) | **必填** — sp-backend 根 URL(须含 `http://` 或 `https://`)。环境变量回退:`SP_API_URL`。录制、回放、Mock、对比,**以及关联日志导出**(`{sp.api.url}/v1/logs`)。 |
当 `sp.api.url` 已设置且服务端 [统一日志管道](./installation/server.md#unified-log-pipeline) 已启用时,日志由 sp-backend 内部代理到 Vector — Agent **无需**单独配置 Vector URL。
### 可选:直连 Vector
高级场景(绕过 backend 代理)可设置:
```bash
-Dsp.otel.exporter.otlp.log.endpoint=http://:4320/v1/logs
```
该 JVM 属性优先于 `{sp.api.url}/v1/logs`。
未设置 `sp.api.url`(且未设置上述覆盖)时,录制与回放仍可用,但应用日志不会导出,该 trace 的 `sp logs` 将为空。
Agent 也可能从 jar 名或环境自动解析 app id;显式设置 `-Dsp.app.id` 可避免录制与回放 id 不一致。旧文档中的 **`sp.service.name`** 在部分部署中仍作别名;新环境请优先使用 **`sp.app.id`**。
## 环境标签
为录制流量打标签,便于筛选与限定回放范围:
```bash
-Dsp.mocker.tags=env=staging
```
录制数据会带上 `env=<值>`,从而只回放特定环境的用例。策略里用 `selector.envTags` 匹配同一标签——见 [策略 YAML 指南 · 通用字段](/zh/testing/policy-yaml-guide#common-fields)。
## 其他部署方式
### `sp.agent.conf` 配置文件
```properties title="META-INF/sp/sp.agent.conf(打包进 agent JAR)"
sp.api.url=http://127.0.0.1:8090
```
一体化与 Helm 部署会在打包时烘焙该配置;运维通常只需 `-javaagent` 与 `-Dsp.app.id`。指向其他后端时用 `SP_API_URL` 或 `-Dsp.api.url` 覆盖。
### Tomcat / `JAVA_OPTS`
在 `catalina.sh` 或 `JAVA_TOOL_OPTIONS` 中设置 Agent 参数,使每个工作 JVM 启动时自动加载。
### 与 OpenTelemetry 共存
若与其他 `-javaagent`(如 OpenTelemetry)冲突,可添加忽略前缀:
```bash
-Dsp.ignore.type.prefixes=io.opentelemetry
-Dsp.ignore.classloader.prefixes=io.opentelemetry
```
多个前缀用英文逗号分隔。
### 调试日志
```bash
-Dsp.enable.debug=true
```
## Agent 状态
`sp app status ` 根据实例心跳返回 **`online`**、**`offline`** 或 **`never`**(默认约 60 秒无心跳视为 offline)。状态反映 Agent 是否在运行,而非仅是否注册了应用。
录制时,旧版界面曾用 **WORKING** / **SLEEPING** / **UNSTART** 表示实例状态;含义相同:必须注入 Agent 且开启录制才会产生用例。
## 完整用例应包含什么
健康的录制用例通常包括:
* **Servlet**(或其他入口类型)— 主 API 请求/响应
* **Database**、**Redis**、**HttpClient** 等 — 按调用顺序的依赖 mocker
* **DynamicClass**(可选)— 已配置的缓存/时间/加解密方法
有流量后列出用例:`sp record case list --app --json`。
## 生产环境保护
为降低对线上流量的影响,Agent 在过载或存储异常时会**背压**。
### 队列溢出
1. 录制任务先进入内存队列(默认容量 **1024**)。
2. 队列满则立即停止录制。
3. 约 30 秒后健康任务将采样率降低约 20% 并重试。
4. 若约 5 分钟后仍满,继续降频直至最低(约每小时一次)。
5. 队列恢复后(约 10 分钟)恢复正常录制。
### 存储健康
1. 调用 sp-storage 失败或超时时立即停止录制。
2. 约 10 秒后恢复录制并采样服务健康度。
3. 若约 3 分钟内仍不健康,按与队列溢出类似的方式逐步降频,直至存储恢复。
配合[录制策略](/zh/testing/policies)中的采样与脱敏,可将生产风险控制在可接受范围。
## 回放侧 Agent
接收回放流量的实例也必须挂载**同一** Agent JAR。专用回放机上请将录制设为关闭或极低,避免在回放过程中误录大量新流量。
## 下一步
Agent 挂上、`sp app status` 显示 online 之后,接入就完成了 → 进入核心流程 **[录制流量](/zh/testing/recording)**。
相关:[下载 Java Agent](/zh/testing/download-java-agent) · [支持的框架](/zh/testing/supported-frameworks) · [快速开始](/zh/testing/getting-started)
---
---
url: /zh/testing/reference/json-types.md
---
# JSON types
CLI-level envelope: see [Output contract](/zh/testing/agents/output-contract).
## Common `data` shapes
### ApplicationListItem
Used in `sp app list` → `data.items[]`.
```json
{
"appId": "string",
"appName": "string",
"name": "string",
"agentStatus": "online | offline | never",
"lastSeenAt": 0,
"agentVersion": "string",
"env": "string",
"tags": ["string"],
"worktreeDirectory": "string"
}
```
### ApplicationCreateResult
Used in `sp app create` → `data`.
```json
{
"success": true,
"appId": "string",
"msg": "string"
}
```
### AgentStatus
Used in `sp app status` → `data`.
```json
{
"appId": "string",
"status": "online | offline | never",
"instanceCount": 0,
"lastSeenAt": 0,
"agentVersion": "string"
}
```
### PolicyValidateResult
```json
{
"valid": true,
"errors": [{ "path": "spec.sampling.rate", "message": "..." }],
"warnings": []
}
```
### ReplayPlanCreated
```json
{
"planId": "string",
"result": 1,
"desc": "string"
}
```
### ReplayProgress
```json
{
"planId": "string",
"status": "RUNNING | FINISHED | FAILED | CANCELLED",
"percent": 0,
"finished": false
}
```
### TraceSummary
```json
{
"traceId": "string",
"endpoint": "string",
"status": "string",
"durationMs": 0,
"startedAt": "ISO-8601",
"attrs": { "orderId": "ORD-123" }
}
```
### ArtifactResult
```json
{
"artifact": "relative/path.json",
"summary": {}
}
```
### PaginatedList
```json
{
"items": [],
"page": 1,
"pageSize": 20,
"total": 0,
"hasMore": false
}
```
## Backend passthrough
When `--include-backend` (optional debug flag) is set, implementers may add `data._backend` with the raw `Response.body` or `CommonResponse.data`.
Agents should not depend on `_backend` in production skills.
## schemaVersion
Future CLI releases may add top-level `"schemaVersion": 1` in the envelope. Agents must ignore unknown top-level fields.
---
---
url: /en/testing/commands/log-query-fields.md
---
# Log query fields
**When agents use this:** Interpret rows returned by [`sp logs`](./logs) or `GET /api/recorder/logs` — field names, meanings, and when correlation ids may be missing.
This reference describes **CLI and API query output** only. It does not document Parquet file paths, partition layout, or how to query storage directly. Use [`sp logs`](./logs) or the canonical HTTP API for all lookups.
**Lookup key:** v1 accepts **`trace_id` only**. Optional correlation labels may appear on rows when ingested — they are not filter keys.
**Naming:** API and Parquet rows use **unprefixed** column names (`source`, `replay_id`, …). OTLP on the wire may use `sp.source`, `sp.replay_id`, etc. before Vector maps them into storage.
***
## Where rows appear
Each successful lookup returns one **chronological stream** of rows in `data.rows` (CLI `--json`) or the API `rows` array. Rows are ordered by event `timestamp` ascending.
Human-readable CLI output prints the same logical fields as API JSON.
See [sp logs](./logs) for the `--trace-id` lookup key, triage workflow, and required time bounds.
***
## Field reference
Every row includes the core fields below. Correlation fields are included **when the emitting runtime knew them at log time** — they may be absent on contextless lines (see [Absent correlation fields](#absent-correlation-fields)).
| Field | Always present | Description |
|-------|----------------|-------------|
| `timestamp` | yes | Event time of the log line. ISO-8601 UTC in JSON (for example `2026-06-27T10:00:10.123Z`). Used for chronological ordering and for caller `[since, until)` filtering. |
| `severity` | yes | Normalized severity text from the emitting logger (for example `DEBUG`, `INFO`, `WARN`, `ERROR`). Each component controls which severities it emits through **its own native logging configuration** — Softprobe does not impose a product-wide severity filter. |
| `body` | yes | Full log message text. v1 returns the complete `body`; query results do not truncate message content. |
| `service_name` | yes | Runtime service identity for the line (for example `travel-ota`, `sp-backend`). Identifies which process produced the row. |
| `source` | yes | Which v1 pipeline source produced the row. Fixed values: `agent`, `app`, or `backend` (see [source values](#source-values)). |
| `trace_id` | when known | W3C OpenTelemetry trace id for the request or work unit that was active when the line was emitted. **The only v1 lookup key.** |
| `span_id` | when known | OpenTelemetry span id for the active span when the line was emitted. |
| `replay_id` | when known | One replay **attempt** id when replay context was active at emit time. Optional label — not a query key. |
| `plan_id` | when known | Replay **plan** id when plan context was active at emit time. Optional label — not a query key. |
| `plan_item_id` | when known | One case or operation inside a replay plan. Optional label — not a query key. |
**Not in v1 query results:** `session_id` / `sp.session_id`.
***
## `source` values
| Value | Meaning | Typical `service_name` examples |
|-------|---------|--------------------------------|
| `agent` | Java agent self-diagnostics (instrumentation, export, internal agent logging) | Application service name under instrumentation |
| `app` | Application-under-test logs captured by the agent (Logback, Log4j2, JUL) | `travel-ota`, customer app id |
| `backend` | sp-backend diagnostic logs exported through OpenTelemetry | `sp-backend` |
Filter or scan by `source` when you only want application lines versus agent diagnostics versus backend lines in the same trace lookup.
```bash
jq '[.rows[].source] | group_by(.) | map({source: .[0], n: length})' /tmp/sp-logs.json
jq -r '.rows[] | select(.source=="backend") | .body' /tmp/sp-logs.json | head -20
```
***
## Absent correlation fields
Correlation fields (`trace_id`, `span_id`, `replay_id`, `plan_id`, `plan_item_id`) are **omitted or empty when the runtime genuinely had no request or replay context** at emit time. This is expected — not a query defect.
Common cases:
| Situation | Typical absent fields | Why |
|-----------|----------------------|-----|
| Process **startup** or **shutdown** | Some or all correlation fields | No active HTTP/RPC request or replay dispatch yet, or context already cleared |
| **Background / housekeeping** lines | `trace_id`, `span_id`, replay/plan ids | Thread or timer work outside record/replay traffic |
| **Agent or backend idle** diagnostics | `replay_id`, `plan_id`, `plan_item_id` | Diagnostic line with trace context only, or no inbound W3C context |
| **Plan context not set** on a line | `plan_id`, `plan_item_id` | Line emitted outside a plan item dispatch even during replay |
When diagnosing a failed replay, query by the **`trace_id`** from the replay case or pytest correlation block. Filter rows by optional `replay_id` in local output when you need replay-scoped lines.
**Case-scoped diagnosis:** when the case row includes **`recordTime`** (API `requestDateTime`) and **`replayTime`**, use two ±2 minute windows (one per anchor) instead of one span from record to replay. The replay window usually has the relevant lines; the record window is often empty. See [sp logs — Case-scoped lookup](./logs#case-scoped-lookup-dual-windows).
***
## Per-component logging ownership
| `source` | Who controls `severity` and what gets emitted |
|----------|-----------------------------------------------|
| `agent` | Agent / JVM logging configuration (`sp.log.path`, `sp.log.console`, `sp.enable.debug`, etc.) |
| `app` | Application Logback, Log4j2, or JUL settings |
| `backend` | sp-backend logging and OpenTelemetry log export configuration |
Softprobe attaches correlation ids and forwards lines each logger already emits. It does not change application log levels or filter severities product-wide.
***
## Example row (JSON)
From [`sp logs --json`](./logs) or `GET /api/recorder/logs`:
```json
{
"timestamp": "2026-06-27T10:00:10.123Z",
"severity": "WARN",
"body": "Replay comparison mismatch",
"service_name": "sp-backend",
"source": "backend",
"trace_id": "2057ad46a7ce03d3955385f2a4142d29",
"span_id": "8d10c94a2a6f4e11",
"replay_id": "6891fd300c676b31",
"plan_id": "6a3f2aad59f0c4655b0f99da",
"plan_item_id": "6a3f2aad59f0c4655b0f99da:1"
}
```
A startup line from the same service might omit correlation fields entirely:
```json
{
"timestamp": "2026-06-27T09:59:55.000Z",
"severity": "INFO",
"body": "Started SpBootApplication in 4.2 seconds",
"service_name": "sp-backend",
"source": "backend"
}
```
***
## Out of scope (v1)
This reference covers unified pipeline **query output** only. Not part of v1 unless separately specified:
* Record trace tables, metrics tables, replay read migration, historical backfill
* Non-replay-path service logs (dashboard, auth, and other Helm/workspace services)
* Direct Parquet file access, object-store credentials, or standalone query tools
***
## Related
* [sp logs](./logs) — command reference, flags, triage, and API mapping
* [Log correlation IDs — find and use ids](/en/testing/reference/log-correlation-ids)
* [Diagnose replay failure example](/en/testing/examples/agent-diagnose-replay)
---
---
url: /zh/testing/agents/output-contract.md
---
# Output contract
All public `sp` commands follow this contract when `--json` is set.
## CLI envelope (stdout on success)
```json
{
"ok": true,
"command": "app list",
"data": { }
}
```
| Field | Type | Description |
|-------|------|-------------|
| `ok` | boolean | Always `true` on exit 0 |
| `command` | string | Normalized command name for logging |
| `data` | object | Command-specific payload (see [JSON types](/zh/testing/reference/json-types)) |
## CLI envelope (stderr on failure, exit 1)
```json
{
"ok": false,
"command": "replay run",
"error": {
"code": "API_ERROR",
"message": "Human-readable summary",
"httpStatus": 500,
"backend": { }
}
}
```
`backend` optionally contains the raw SoftProbe `Response` or schedule `CommonResponse` body for debugging.
## Backend response shapes
### sp-tr-api (`Response`)
Most console APIs return:
```json
{
"responseStatusType": { "responseCode": 0, "responseDesc": "success" },
"body": { }
}
```
The CLI maps `responseCode !== 0` to exit code `1`.
### sp-replay-schedule (`CommonResponse`)
Replay control (`createPlan`, `progress`, …) uses:
```json
{
"result": 1,
"desc": "success",
"data": { }
}
```
The CLI maps `result !== 1` (per server convention) to exit code `1`.
## Artifacts (large output)
When response bodies exceed an internal threshold (recommended: 64 KiB) or when the command always produces files (diff detail, log download):
**stdout:**
```json
{
"ok": true,
"command": "replay diff get",
"data": {
"artifact": ".sp-work/diff-abc123.json",
"summary": {
"diffId": "abc123",
"diffResultCode": 1,
"categoryName": "..."
}
}
}
```
Agents should **read the artifact file** with their file tool, not expect full payloads on stdout.
Default `--out-dir`: `.sp-work/` in the current working directory (override per invocation).
## Pagination
List commands accept:
| Flag | Default | Description |
|------|---------|-------------|
| `--page` | `1` | 1-based page index |
| `--limit` | `20` | Page size (max 100 unless documented) |
| `--fields` | all | Comma-separated field filter |
Paginated JSON:
```json
{
"ok": true,
"command": "replay case list",
"data": {
"items": [],
"page": 1,
"pageSize": 20,
"total": 142,
"hasMore": true
}
}
```
## Human-readable mode (no `--json`)
* Tables on stdout
* Errors on stderr as plain text
* Colors only when stdout is a TTY and `--pretty` is set (optional; not required for v1 implementers)
## Non-interactive rule
If `--json` is set and authentication is missing, the CLI must **not** prompt. Exit `3` with:
```json
{
"ok": false,
"error": { "code": "AUTH_REQUIRED", "message": "Set SP_TOKEN or run sp auth login" }
}
```
---
---
url: /en/platform.md
---
# Softprobe Platform
**Zero code changes · Full-context visibility · Cost optimization**
::: info
Softprobe captures every user journey as a session graph—making interactions analyzable, automation-ready, and economical to retain.
:::
## Quick links
* [Quick Start](/en/platform/getting-started/quick-start)
* [Production Installation](/en/platform/deployment/installation)
* [SESSIFY](/en/platform/sessify)
* [Dashboard User Guide](/en/platform/production/dashboard-user-guide)
## Record and replay (Java)
For Java traffic capture, replay, and diff, see [Softprobe Testing](/en/testing/getting-started). Automate with the [CLI quickstart](/en/testing/getting-started).
## How it works
* **Server-side:** Wasm plugin in Istio Envoy sidecar — [SP-Istio Agent on GitHub](https://github.com/softprobe/softprobe)
* **Client-side:** [SESSIFY](/en/platform/sessify) enriches sessions with routes, metrics, and interaction events

::: tip Currently available
* Data collection: SESSIFY and Istio/Envoy OpenTelemetry traces
* Visualization: Context View (session graph)
:::

---
---
url: /en/testing/reference.md
---
# Reference
Reference material for Testing automation:
* API mapping
* JSON output envelopes
* Exit codes
* Replay send log markers
---
---
url: /zh/platform/sessify.md
---
# SESSIFY 集成
**轻量 • 零依赖 • 符合 W3C 标准的分布式追踪**
SESSIFY 是 Softprobe 的会话生命周期管理与分布式追踪 SDK,专为前端应用的会话管理与请求追踪设计。它自动处理会话的创建、校验、保活与过期逻辑,并向 HTTP 请求注入符合 W3C 标准的 trace 上下文,实现端到端的会话关联。
核心收益包括:自动化会话管理、跨微服务的分布式追踪、基于 Web Crypto API 的安全性,以及零依赖带来的最小打包体积。
::: info
快速开始环境已预装并启用了 SESSIFY(`@softprobe/sessify`)。本文档用于将 SDK 集成进你自己的前端应用(React / Vue / Next.js 等)。
:::
## 前置条件
* 一个现代 Web 应用(React、Vue、Next.js 或原生 JavaScript)
* Node.js 16+ 及打包工具(如 Vite、Webpack)
## 安装
```bash
# npm
npm install @softprobe/sessify
# yarn
yarn add @softprobe/sessify
# pnpm
pnpm add @softprobe/sessify
```
## 初始化
```ts
// React / Next.js 示例
import { useEffect } from 'react';
import { initSessify } from '@softprobe/sessify';
useEffect(() => {
// 使用默认配置初始化
initSessify({
});
}, []);
// 原生 JS 示例
import { initSessify } from '@softprobe/sessify';
initSessify({
});
```
## 核心功能
## 核心用法
初始化后,你可以在应用的任意位置管理会话。
```javascript
import { getSessionId, startSession, endSession, isSessionActive } from '@softprobe/sessify';
// 1. 获取当前会话
// 若已过期或不存在,会自动创建一个新会话。
const sessionId = getSessionId();
console.log('Active Session:', sessionId);
// 2. 检查状态
if (isSessionActive()) {
console.log('User is currently active');
}
// 3. 强制刷新(如登录时)
// 立即作废旧会话并开启一个全新会话。
const newSessionId = startSession();
// 4. 登出 / 清理
// 清空存储并作废会话。
endSession();
```
## 会话与上下文传播
* SDK 会在标签页首次初始化时自动生成一个唯一的 sessionId,无需你手动创建。
* 在同一浏览器标签页内进行的页面跳转与交互,都会复用同一个 sessionId,使一次用户访问在时间维度上能够完整串联。
* 打开新的标签页或窗口,会生成新的 sessionId;关闭标签页或重新初始化后,会话也会随之重置。
* 所有上报的数据都会携带该 sessionId,使后端能够将同一次会话中的前端事件与后端服务侧的链路数据进行关联,实现端到端的可观测性与排障效率提升。
## 配置
`initSessify` 函数接受一个配置对象,用于按需定制行为。
| 选项 | 类型 | 默认值 | 说明 |
|--------|------|---------|-------------|
| customTraceState | object | {} | 要包含进 tracestate 头的自定义键值对。 |
| sessionStorageType | session/local | 'session' | 控制持久化。`'local'` 在浏览器重启后仍保留;`'session'` 在标签页关闭时清除。 |
### 自定义 Trace State 示例
通过加入环境、版本等上下文来增强请求追踪:
```javascript
initSessify({
sessionStorageType: 'local',
customTraceState: {
'x-sp-env': 'production',
'x-sp-ver': '1.0.0',
'x-sp-tier': 'premium'
}
});
```
生成的头部:`tracestate: x-sp-session-id=...,x-sp-env=production,x-sp-ver=1.0.0...`
## 技术细节
### 会话生命周期策略
* **创建**:使用 base36 时间戳结合 Web Crypto API 随机串,生成约 16 字符的唯一 ID。
* **校验**:每次访问自动检查是否超时。一旦超过超时时间,旧会话被丢弃并无缝生成新会话。
### HTTP 拦截
@softprobe/sessify 会(安全地)对全局 fetch API 打补丁,以便:
* 检查会话是否活跃。
* 注入符合 W3C 标准的 tracestate 头。
* 附加你的 siteName 或 customTraceState 以及当前 session\_id。
---
---
url: /en.md
---
::: info
Two product areas, one site — **Testing** (Java record/replay plus `sp` automation) and **Business Observability** (Istio/SESSIFY). Pick the path that matches your role above.
:::
---
---
url: /zh.md
---
::: info
同一站点包含 **测试**(Java 录制/回放和 `sp` 自动化)与 **业务观测**(Istio/SESSIFY)两类产品区域,请按上方角色选择路径。
:::
---
---
url: /zh/platform/production/dashboard-user-guide.md
---
# Softprobe 仪表盘使用指南
欢迎使用 Softprobe 仪表盘。本指南帮助你快速熟悉界面、完成租户与成员管理,并稳定地使用核心功能。
## 🚀 快速开始
### 1. 注册与登录
* 访问首页,点击右上角“Sign Up”
* 输入邮箱与密码,完成邮件验证
* 登录并开始使用系统
::: tip
账号设置与公钥管理可参见 [账号设置指南](/zh/platform/getting-started/account-setup)。
:::
### 2. 界面总览
登录后,你将看到:
* **左侧导航**:模块快速入口
* **顶部栏**:用户信息与租户切换
* **主工作区**:当前模块的操作界面
## 📊 核心功能
### 1. 仪表盘首页
实时监控系统核心指标
#### 数据概览卡片
* **数据库使用量**:当前存储用量(GB)、使用比例与颜色提示
* **数据写入统计**:总记录数、时间范围筛选与历史对比
* **会话统计**:活跃会话、所选时间段总会话、趋势图
* **性能指标**:平均响应时间、P95 指标、系统健康状况
::: tip
如需实现端到端用户旅程关联,请在前端安装 [SESSIFY](/zh/platform/sessify) 并通过服务网格传播 sessionId。
:::
#### 操作与更新
* 页面进入时加载一次数据(不自动刷新)
* 更新方式:切换时间范围、切换租户或手动刷新页面
### 2. 租户管理
面向多租户环境的资源隔离与管理
#### 租户切换
* 使用顶部栏租户选择器(支持搜索与筛选)
* 点击目标租户完成切换
* 通过“+”创建新租户并填写基本信息
#### 租户设置
* 基本信息:名称、描述、图标
* 成员管理:添加/移除、角色与权限
* API 密钥:创建/管理密钥与有效期
* 危险操作区:删除租户、导出备份、清理数据
### 3. 团队成员管理
权限控制与协同管理
#### 添加成员
* 进入 租户设置 → 成员管理
* 点击“Add Member”,输入邮箱并选择角色
* 角色类型:**管理员**、**编辑者**、**查看者**
#### 角色权限
* **管理员**:拥有完整控制权限;可管理成员/设置、API 密钥与危险操作
* **编辑者**:可进行数据与监控配置的操作;不可管理成员与全局设置
* **查看者**:只读访问;适合报表查看与审计
## ⚠️ 常见限制与最佳实践
* 数据刷新:仪表盘首页不自动刷新;请切换时间范围或租户,或手动刷新更新数据。
* 角色与安全:遵循最小权限原则;避免对所有成员授予“管理员”;定期审查成员列表。
* API 密钥:完整密钥仅在创建时展示;请妥善保存并定期轮换;切勿提交到代码仓库。
* 速率限制:每个 API 密钥有每小时上限;客户端应实现重试/退避,并在仪表盘监控用量计数。
* PII 处理:避免在属性中发送敏感个人信息;使用哈希后的请求体进行关联。
* 多环境隔离:为生产/预发/开发分别创建租户与公钥,提升隔离性与审计便利性。
* 性能建议:结合时间过滤与 request\_body\_hash 使用;数据集增大时配合分区与缓存策略。
::: tip 快速提醒
若需要更高配额或自定义保留周期,请携带租户 ID 与预期负载邮件联系 support@softprobe.ai。
:::
## ❓ 常见问题
参阅 [FAQ](/zh/platform/support/faq),包含数据与安全、移动端访问与导出等常见问题。
## ➡️ 下一步
* 学习核心概念:[理解核心概念](/zh/platform/advanced-guides/concepts)
* 部署生产环境:[安装指南](/zh/platform/deployment/installation)
* 配置高级规则:[配置参考](/zh/platform/configuration/config)
***
::: info 提示
部署完成并产生流量后,打开 Context View 即可查看端到端会话图谱、性能指标与交互事件。
:::
***
***
最后更新:2024 年 12 月
---
---
url: /zh/testing.md
---
# Softprobe 测试
**录制真实流量。回放时自动 Mock。无需手写用例即可对比。**
::: tip 准备开始?
🚀 如果您是首次接触 Softprobe,建议直接阅读 **[快速开始](/zh/testing/getting-started)** 指南!在 5 分钟内通过预构建的 JAR 快速体验,并无缝接入您自己的应用程序。
:::
Softprobe 测试面向 **Java** 服务的**录制回放**回归。以 `-javaagent` 挂载 Softprobe Java Agent,在后台采集入口 API 流量与对外依赖调用,随后在测试环境回放已录制用例:依赖由存储数据 Mock,结果自动对比。
::: info 本站产品区域
| 区域 | 适用场景 |
|------|----------|
| **[平台](/zh/platform/)** | 需要 Istio/Envoy 网格采集、SESSIFY 会话上下文或可观测性仪表盘 |
| **测试**(本节) | 需要 Java 录制回放、JVM Agent、策略、回放语义、安装、命令与自动化 |
:::
## 为什么选择录制回放
传统集成测试需要维护环境、造数与手写用例。Softprobe 测试则:
* **零业务代码改动** — 通过 `sp-agent.jar` 做字节码织入
* **真实流量带来高覆盖** — 生产或预发请求直接成为回放用例
* **回放环境隔离** — 数据库、HTTP 客户端、Redis、RPC、缓存等由录制数据 Mock,测试环境无需真实下游
* **WRITE 路径更安全** — 回放依赖行为不会污染共享库表
* **降低对比噪声** — 对比规则、时间 Mock、忽略节点处理时间戳、随机 ID 与环境差异字段
## 组件分工
| 组件 | 作用 |
|------|------|
| **你的 Java 服务** | 被测应用,以 `-javaagent:…/sp-agent.jar` 启动 |
| **Softprobe Java Agent** | 运行时录制与回放;回放阶段 Mock 依赖 |
| **Softprobe 后端**(`:8090`) | 通过 Helm 部署。存储用例(MongoDB)、下发策略、执行回放计划、计算差异 |
| **`sp` 命令**(可选) | 注册应用、应用策略、发起回放、排查失败 — 见 [命令](/zh/testing/commands/) |
| **仪表盘 / 工作台**(可选) | 可视化差异与链路查看 |
```mermaid
flowchart LR
App[JVM 被测应用]
Agent[Java Agent]
Backend[sp-backend]
App --> Agent
Agent --> Backend
```
## 录制 → 回放 → 对比(简述)
1. **录制** — Agent 在应用处理真实请求时采集入口流量(如 HTTP `Servlet`)与依赖调用(`HttpClient`、`Database`、`Redis` 等)。
2. **存储** — 后端持久化每次交互;回放热路径使用 Redis Mock 缓存。
3. **回放** — 调度服务将录制的入口请求发到**测试实例**(`targetEnv` URL);Agent 返回录制的依赖响应,不访问真实下游。
4. **对比** — 引擎对比录制与回放流量;策略定义忽略项与匹配严格度。
详情:[录制流量](/zh/testing/recording) · [工作原理](/zh/testing/how-it-works) · [回放与对比](/zh/testing/replay-and-diff)
## 平台 Agent ≠ Java Agent
[平台](/zh/platform/advanced-guides/agent-architecture) 的 **SP-Istio Agent** 运行在 Envoy 侧车,采集网格 HTTP 流量。**Softprobe 测试**使用以 `-javaagent` 挂载的 **JVM Agent**。二者解决的问题不同;在 SaaS 部署中可同时接入同一后端做关联分析。
## 适合阅读本节的人
* 首次接触录制回放的 **Java 开发者**
* 无需完整下游栈即可做回归的 **QA / 发布工程师**
* 在 K8s 或镜像中配置 `sp-backend` 与 Agent 启动的 **平台工程师**
* **自动化作者** — 先理解概念,再看 [命令](/zh/testing/commands/) 中的 `sp` 自动化约定
## 快速链接
* [快速开始](/zh/testing/getting-started) — 5分钟内体验 [Travel OTA](https://github.com/softprobe/demo-ota) 演示并快速接入您自己的应用。
* [安装 Softprobe](/zh/testing/installation/) — 使用 `sp` 安装、设置、启动编码、诊断与升级。
* [录制流量](/zh/testing/recording) — 核心流程第 1 步:产生用例。
* [Java Agent](/zh/testing/java-agent) — 挂载、JVM 参数与生产安全。
* [策略概览](/zh/testing/policies) — 按阶段配置 YAML。
* [回放与对比](/zh/testing/replay-and-diff) — 核心流程第 2 步:回归运行。
* [Webhook 与 CI/CD](/zh/testing/webhook-and-ci) — 部署后触发回放与流水线门禁。
* [支持的框架](/zh/testing/supported-frameworks)
---
---
url: /en/testing/commands/agent.md
---
# sp agent
**When agents use this:** Install a backend-compatible `sp-agent.jar` and get copy-paste JVM flags after `sp app create`.
## Synopsis
| Subcommand | Description |
|------------|-------------|
| `command` | Emit `-javaagent` and `sp.*` system properties for record mode |
## `agent command`
On **Softprobe Cloud**, run `sp tenant key ensure` once (or let this command auto-create the key). The output includes `-Dsp.api.token=` for the Java agent.
```bash
sp tenant key ensure --json # SaaS: once per tenant
curl -fsSL -o sp-agent.jar https://install.softprobe.ai/artifacts/agent/latest/sp-agent.jar
sp agent command --app a1b2c3d4e5f67890 --json
sp agent command --app a1b2c3d4e5f67890 --agent-jar ./sp-agent.jar --app-jar target/app.jar --json
```
| Flag | Description |
|------|-------------|
| `--app` | Required. Registered `appId` from `sp app create` |
| `--agent-jar` | Optional. Default: `$SP_AGENT_JAR`, then `${XDG_DATA_HOME}/softprobe/agent/sp-agent.jar` |
| `--app-jar` | Optional. Trailing `-jar …` in `startCommand` |
| `--format` | `json` (default), `shell`, `docker`, `maven` |
Download `sp-agent.jar` from [Download Java agent](/en/testing/download-java-agent). Available immutable versions are published under `https://install.softprobe.ai/artifacts/agent//sp-agent.jar`.
`apiUrl` in the JSON output comes from the resolved CLI profile (`api_url` / `SP_API_URL`). Override with `sp config set-url`, `SP_API_URL`, or the global `--api-url` flag before `agent command`.
Precedence for the agent at runtime (not the CLI): JVM `-Dsp.api.url` → env `SP_API_URL` → bundled `META-INF/sp/sp.agent.conf` in the agent JAR.
When the default jar is missing:
```json
{
"ok": false,
"command": "agent command",
"error": {
"code": "USAGE",
"message": "sp-agent.jar not found",
"backend": {
"nextActions": ["Download sp-agent.jar from https://install.softprobe.ai/artifacts/agent/latest/sp-agent.jar"]
}
}
}
```
Example success `data` (abbreviated):
```json
{
"appId": "a1b2c3d4e5f67890",
"agentJar": "/home/user/.local/share/softprobe/agent/sp-agent.jar",
"agentJarExists": true,
"apiUrl": "http://127.0.0.1:8090",
"jvmArgs": [
"-javaagent:/home/user/.local/share/softprobe/agent/sp-agent.jar",
"-Dsp.app.id=a1b2c3d4e5f67890",
"-Dsp.api.url=http://127.0.0.1:8090",
"-Dsp.api.token=…"
],
"startCommand": "java -javaagent:… -Dsp.app.id=… …",
"startCommandMultiline": "java \\\n -javaagent:… \\\n …",
"dockerJavaToolOptions": "-javaagent:… …",
"mavenArgLine": "-javaagent:… …",
"nextActions": [
"Run startCommand (or paste startCommandMultiline into a run script)",
"Send traffic, then: sp record case list --app a1b2c3d4e5f67890 --since -1h --json"
]
}
```
| `--format` | stdout |
|------------|--------|
| `json` | Full envelope on stdout |
| `shell` | `startCommandMultiline` only |
| `docker` | `ENV JAVA_TOOL_OPTIONS='…'` |
| `maven` | `…` |
## Related
* [Doctor](/en/testing/installation/doctor) — `sp doctor`
* [app](./app) — create app and check heartbeat
* [record](./record) — list recorded cases
* [Concepts: Java agent](/en/testing/agents/concepts)
---
---
url: /zh/testing/commands/agent.md
---
# sp agent
**When agents use this:** Install a backend-compatible `sp-agent.jar` and get copy-paste JVM flags after `sp app create`.
## Synopsis
| Subcommand | Description |
|------------|-------------|
| `command` | Emit `-javaagent` and `sp.*` system properties for record mode |
## `agent command`
在 **Softprobe Cloud** 上,先运行一次 `sp tenant key ensure`(或让本命令自动创建 key)。输出中会包含供 Java agent 使用的 `-Dsp.api.token=`。
```bash
sp tenant key ensure --json # SaaS:每个租户一次
curl -fsSL -o sp-agent.jar https://install.softprobe.ai/artifacts/agent/latest/sp-agent.jar
sp agent command --app a1b2c3d4e5f67890 --json
sp agent command --app a1b2c3d4e5f67890 --agent-jar ./sp-agent.jar --app-jar target/app.jar --json
```
| Flag | Description |
|------|-------------|
| `--app` | Required. Registered `appId` from `sp app create` |
| `--agent-jar` | Optional. Default: `$SP_AGENT_JAR`, then `${XDG_DATA_HOME}/softprobe/agent/sp-agent.jar` |
| `--app-jar` | Optional. Trailing `-jar …` in `startCommand` |
| `--format` | `json` (default), `shell`, `docker`, `maven` |
请从 [下载 Java Agent](/zh/testing/download-java-agent) 下载 `sp-agent.jar`。不可变版本发布在 `https://install.softprobe.ai/artifacts/agent//sp-agent.jar`。
JSON 中的 `apiUrl` 来自 CLI 配置(`api_url` / `SP_API_URL`)。可通过 `sp config set-url`、`SP_API_URL` 或全局 `--api-url` 覆盖。
Agent 运行时解析顺序:JVM `-Dsp.api.url` → 环境变量 `SP_API_URL` → JAR 内嵌 `META-INF/sp/sp.agent.conf`。
When the default jar is missing:
```json
{
"ok": false,
"command": "agent command",
"error": {
"code": "USAGE",
"message": "sp-agent.jar not found",
"backend": {
"nextActions": ["从 https://install.softprobe.ai/artifacts/agent/latest/sp-agent.jar 下载 sp-agent.jar"]
}
}
}
```
Example success `data` (abbreviated):
```json
{
"appId": "a1b2c3d4e5f67890",
"agentJar": "/home/user/.local/share/softprobe/agent/sp-agent.jar",
"agentJarExists": true,
"apiUrl": "http://127.0.0.1:8090",
"jvmArgs": [
"-javaagent:/home/user/.local/share/softprobe/agent/sp-agent.jar",
"-Dsp.app.id=a1b2c3d4e5f67890",
"-Dsp.api.url=http://127.0.0.1:8090",
"-Dsp.api.token=…"
],
"startCommand": "java -javaagent:… -Dsp.app.id=… …",
"startCommandMultiline": "java \\\n -javaagent:… \\\n …",
"dockerJavaToolOptions": "-javaagent:… …",
"mavenArgLine": "-javaagent:… …",
"nextActions": [
"Run startCommand (or paste startCommandMultiline into a run script)",
"Send traffic, then: sp record case list --app a1b2c3d4e5f67890 --since -1h --json"
]
}
```
| `--format` | stdout |
|------------|--------|
| `json` | Full envelope on stdout |
| `shell` | `startCommandMultiline` only |
| `docker` | `ENV JAVA_TOOL_OPTIONS='…'` |
| `maven` | `…` |
## Related
* [Doctor](/zh/testing/installation/doctor) — `sp doctor`
* [app](./app) — create app and check heartbeat
* [record](./record) — list recorded cases
* [Concepts: Java agent](/zh/testing/agents/concepts)
---
---
url: /en/testing/commands/app.md
---
# sp app
**When agents use this:** Resolve `appId`, check agent connectivity, register a service, or list recent replay plans for an app.
## Synopsis
List, create, and inspect SoftProbe **applications**. An application is a registered service; recording, replay, and policies are scoped by `appId`. See [Concepts — Application](/en/testing/agents/concepts#application-appid).
Use `--json` on all subcommands. See [Output contract](/en/testing/agents/output-contract).
## Subcommands
| Subcommand | Args | Description |
|------------|------|-------------|
| `list` | — | Applications visible to the caller, with agent status |
| `create` | `` | Register a new application (server assigns `appId`) |
| `status` | `` | Agent connectivity for one application |
| `replays` | `` | Recent replay plans (`--limit`) |
## `list`
```bash
sp app list --json
```
**REST:** `GET /api/applications/list`
Requires a token when `--json` is set (CLI exits with an auth error if `SP_TOKEN` is unset). Results are filtered by user group, ownership, and cross-group grants; see [Authentication](/en/testing/agents/authentication).
### JSON output
`data.items` is an array of list rows. Each row merges app config with live agent status (heartbeat threshold defaults to 60s → `offline`).
| Field | Description |
|-------|-------------|
| `appId` | Stable id for agent config and other `sp` commands |
| `appName`, `name` | Display name |
| `agentStatus` | `online`, `offline`, or `never` |
| `lastSeenAt` | Unix ms, freshest instance heartbeat |
| `agentVersion` | Agent build string |
| `env` | Primary env tag, or `production` |
| `tags` | Flattened tag values |
| `worktreeDirectory` | Optional workspace path |
```json
{
"ok": true,
"command": "app list",
"data": {
"items": [
{
"appId": "a1b2c3d4e5f67890",
"appName": "order-service",
"agentStatus": "online",
"lastSeenAt": 1747564800000
}
]
}
}
```
## `create`
```bash
sp app create order-service-staging --json
```
**REST:** `POST /api/applications/create`
`appName` must be unique. The CLI sends `{ "appName": "" }`; the server may set `owners` from the JWT when omitted.
### Request body (REST)
| Field | Required | Description |
|-------|----------|-------------|
| `appName` | Yes | Unique name (CLI positional) |
| `owners` | No | Owner user names; defaults to current user |
| `visibilityLevel` | No | `0` public, `1` private |
| `groupId`, `groupName` | No | User group assignment |
### JSON output
| Field | Description |
|-------|-------------|
| `success` | Whether creation succeeded |
| `appId` | Generated id — use in agent and `sp replay run --app` |
| `msg` | Detail when `success` is false |
```json
{
"ok": true,
"command": "app create",
"data": {
"success": true,
"appId": "f3e2d1c0b9a87654"
}
}
```
## `status`
```bash
sp app status f3e2d1c0b9a87654 --json
```
**REST:** `GET /api/applications/{appId}/agent-status`
Aggregates JVM instance heartbeats for the app. `status` reflects the **freshest** instance:
| Value | Meaning |
|-------|---------|
| `never` | No instances reported |
| `online` | Latest heartbeat within threshold |
| `offline` | Instances exist but heartbeat is stale |
| Field | Description |
|-------|-------------|
| `appId` | Application id |
| `status` | `never`, `online`, or `offline` |
| `instanceCount` | Registered instances |
| `lastSeenAt` | Unix ms |
| `agentVersion` | From freshest instance |
```json
{
"ok": true,
"command": "app status",
"data": {
"appId": "f3e2d1c0b9a87654",
"status": "online",
"instanceCount": 2,
"lastSeenAt": 1747564800000
}
}
```
## `replays`
```bash
sp app replays f3e2d1c0b9a87654 --limit 10 --json
```
**REST:** `GET /api/applications/{appId}/replays/recent?limit=N`
| Flag | Default | Description |
|------|---------|-------------|
| `--limit` | `5` | Max plans (CLI rejects < 1; values above 100 are clamped to 100; server also clamps 1–100) |
`data` is an array of plan summaries (`planId`, `planName`, `status`, case counts, `createTime`, `triggeredBy`, …). Use `planId` with [sp replay](replay.md) and [replay case](/en/testing/commands/replay-case).
## REST mapping
| Subcommand | Method | Path |
|------------|--------|------|
| `list` | GET | `/api/applications/list` |
| `create` | POST | `/api/applications/create` |
| `status` | GET | `/api/applications/{appId}/agent-status` |
| `replays` | GET | `/api/applications/{appId}/replays/recent` |
Header: `access-token: `.
## Replaces `sp_api`
| sp\_api endpoint | sp command |
|-----------------|------------|
| `list_applications` | `sp app list` |
| `agent_status` | `sp app status` |
| `recent_replays` | `sp app replays` |
## Related
* [Concepts — Application](/en/testing/agents/concepts#application-appid)
* [Quickstart](/en/testing/getting-started)
* [sp replay](replay.md)
* [Diagnose replay failure](/en/testing/examples/agent-diagnose-replay)
* [JSON types — ApplicationListItem](/en/testing/reference/json-types#applicationlistitem)
* [API mapping](/en/testing/reference/api-mapping)
---
---
url: /zh/testing/commands/app.md
---
# sp app
**When agents use this:** Resolve `appId`, check agent connectivity, register a service, or list recent replay plans for an app.
## Synopsis
List, create, and inspect SoftProbe **applications**. An application is a registered service; recording, replay, and policies are scoped by `appId`. See [Concepts — Application](/zh/testing/agents/concepts#application-appid).
Use `--json` on all subcommands. See [Output contract](/zh/testing/agents/output-contract).
## Subcommands
| Subcommand | Args | Description |
|------------|------|-------------|
| `list` | — | Applications visible to the caller, with agent status |
| `create` | `` | Register a new application (server assigns `appId`) |
| `status` | `` | Agent connectivity for one application |
| `replays` | `` | Recent replay plans (`--limit`) |
## `list`
```bash
sp app list --json
```
**REST:** `GET /api/applications/list`
Requires a token when `--json` is set (CLI exits with an auth error if `SP_TOKEN` is unset). Results are filtered by user group, ownership, and cross-group grants; see [Authentication](/zh/testing/agents/authentication).
### JSON output
`data.items` is an array of list rows. Each row merges app config with live agent status (heartbeat threshold defaults to 60s → `offline`).
| Field | Description |
|-------|-------------|
| `appId` | Stable id for agent config and other `sp` commands |
| `appName`, `name` | Display name |
| `agentStatus` | `online`, `offline`, or `never` |
| `lastSeenAt` | Unix ms, freshest instance heartbeat |
| `agentVersion` | Agent build string |
| `env` | Primary env tag, or `production` |
| `tags` | Flattened tag values |
| `worktreeDirectory` | Optional workspace path |
```json
{
"ok": true,
"command": "app list",
"data": {
"items": [
{
"appId": "a1b2c3d4e5f67890",
"appName": "order-service",
"agentStatus": "online",
"lastSeenAt": 1747564800000
}
]
}
}
```
## `create`
```bash
sp app create order-service-staging --json
```
**REST:** `POST /api/applications/create`
`appName` must be unique. The CLI sends `{ "appName": "" }`; the server may set `owners` from the JWT when omitted.
### Request body (REST)
| Field | Required | Description |
|-------|----------|-------------|
| `appName` | Yes | Unique name (CLI positional) |
| `owners` | No | Owner user names; defaults to current user |
| `visibilityLevel` | No | `0` public, `1` private |
| `groupId`, `groupName` | No | User group assignment |
### JSON output
| Field | Description |
|-------|-------------|
| `success` | Whether creation succeeded |
| `appId` | Generated id — use in agent and `sp replay run --app` |
| `msg` | Detail when `success` is false |
```json
{
"ok": true,
"command": "app create",
"data": {
"success": true,
"appId": "f3e2d1c0b9a87654"
}
}
```
## `status`
```bash
sp app status f3e2d1c0b9a87654 --json
```
**REST:** `GET /api/applications/{appId}/agent-status`
Aggregates JVM instance heartbeats for the app. `status` reflects the **freshest** instance:
| Value | Meaning |
|-------|---------|
| `never` | No instances reported |
| `online` | Latest heartbeat within threshold |
| `offline` | Instances exist but heartbeat is stale |
| Field | Description |
|-------|-------------|
| `appId` | Application id |
| `status` | `never`, `online`, or `offline` |
| `instanceCount` | Registered instances |
| `lastSeenAt` | Unix ms |
| `agentVersion` | From freshest instance |
```json
{
"ok": true,
"command": "app status",
"data": {
"appId": "f3e2d1c0b9a87654",
"status": "online",
"instanceCount": 2,
"lastSeenAt": 1747564800000
}
}
```
## `replays`
```bash
sp app replays f3e2d1c0b9a87654 --limit 10 --json
```
**REST:** `GET /api/applications/{appId}/replays/recent?limit=N`
| Flag | Default | Description |
|------|---------|-------------|
| `--limit` | `5` | Max plans (CLI rejects < 1; values above 100 are clamped to 100; server also clamps 1–100) |
`data` is an array of plan summaries (`planId`, `planName`, `status`, case counts, `createTime`, `triggeredBy`, …). Use `planId` with [sp replay](replay.md) and [replay case](/zh/testing/commands/replay-case).
## REST mapping
| Subcommand | Method | Path |
|------------|--------|------|
| `list` | GET | `/api/applications/list` |
| `create` | POST | `/api/applications/create` |
| `status` | GET | `/api/applications/{appId}/agent-status` |
| `replays` | GET | `/api/applications/{appId}/replays/recent` |
Header: `access-token: `.
## Replaces `sp_api`
| sp\_api endpoint | sp command |
|-----------------|------------|
| `list_applications` | `sp app list` |
| `agent_status` | `sp app status` |
| `recent_replays` | `sp app replays` |
## Related
* [Concepts — Application](/zh/testing/agents/concepts#application-appid)
* [Quickstart](/zh/testing/getting-started)
* [sp replay](replay.md)
* [Diagnose replay failure](/zh/testing/examples/agent-diagnose-replay)
* [JSON types — ApplicationListItem](/zh/testing/reference/json-types#applicationlistitem)
* [API mapping](/zh/testing/reference/api-mapping)
---
---
url: /en/testing/commands/auth.md
---
# sp auth
**When agents use this:** When `SP_TOKEN` is unset or expired.
## Synopsis
Authenticate and inspect identity.
## Subcommands
| Subcommand | Flags | Description |
|------------|-------|-------------|
| `login` | `--email`, `--code`, `--guest`, `--no-save` | Obtain JWT |
| `whoami` | — | Current user from token |
| `refresh` | `--user`, `--no-save` | Refresh token |
## Examples
```bash
sp auth login --email ops@corp.com --code 848291 --json
sp auth login --email ops@corp.com --code 848291 --no-save --json
sp auth whoami --json
export SP_TOKEN="$(sp auth login ... --no-save --json | jq -r '.data.token')"
```
By default, `login` and `refresh` save the token to the shared configuration file in
`${XDG_CONFIG_HOME:-~/.config}/softprobe/config.jsonc`. With `--no-save`, the token
is returned in JSON only and no config, cache, data, or state file is modified.
### JSON output (`login`)
```json
{
"ok": true,
"command": "auth login",
"data": {
"token": "eyJ...",
"userName": "ops@corp.com",
"expiresAt": "2026-05-19T12:00:00Z"
}
}
```
## REST mapping
| Subcommand | Method | Path |
|------------|--------|------|
| `login` (email) | POST | `/api/login/verify` |
| `login` (guest) | POST | `/api/login/loginAsGuest` |
| `refresh` | GET | `/api/login/refresh/{userName}` |
| `whoami` | — | JWT decode locally, or future profile API |
Request body for verify: `VerifyRequestType` (`userName`, `verifyCode`, …).
## Replaces
None (new). CI uses `SP_TOKEN` directly.
## Related
* [Authentication guide](/en/testing/agents/authentication)
---
---
url: /zh/testing/commands/auth.md
---
# sp auth
**When agents use this:** When `SP_TOKEN` is unset or expired.
## Synopsis
Authenticate and inspect identity.
## Subcommands
| Subcommand | Flags | Description |
|------------|-------|-------------|
| `login` | `--email`, `--code`, `--guest`, `--no-save` | Obtain JWT |
| `whoami` | — | Current user from token |
| `refresh` | `--user`, `--no-save` | Refresh token |
## Examples
```bash
sp auth login --email ops@corp.com --code 848291 --json
sp auth login --email ops@corp.com --code 848291 --no-save --json
sp auth whoami --json
export SP_TOKEN="$(sp auth login ... --no-save --json | jq -r '.data.token')"
```
By default, `login` and `refresh` save the token to the shared configuration file in
`${XDG_CONFIG_HOME:-~/.config}/softprobe/config.jsonc`. With `--no-save`, the token
is returned in JSON only and no config, cache, data, or state file is modified.
### JSON output (`login`)
```json
{
"ok": true,
"command": "auth login",
"data": {
"token": "eyJ...",
"userName": "ops@corp.com",
"expiresAt": "2026-05-19T12:00:00Z"
}
}
```
## REST mapping
| Subcommand | Method | Path |
|------------|--------|------|
| `login` (email) | POST | `/api/login/verify` |
| `login` (guest) | POST | `/api/login/loginAsGuest` |
| `refresh` | GET | `/api/login/refresh/{userName}` |
| `whoami` | — | JWT decode locally, or future profile API |
Request body for verify: `VerifyRequestType` (`userName`, `verifyCode`, …).
## Replaces
None (new). CI uses `SP_TOKEN` directly.
## Related
* [Authentication guide](/zh/testing/agents/authentication)
---
---
url: /en/testing/commands/config.md
---
# sp config
**When agents use this:** Once per session to verify connectivity; CI uses env vars instead of `init`.
## Synopsis
Manage XDG-backed SoftProbe config, profiles, and backend URL.
## Subcommands
| Subcommand | Description |
|------------|-------------|
| `init` | Create `${XDG_CONFIG_HOME}/softprobe/config.jsonc` and `sp.jsonc` |
| `show` | Print resolved config sources, active profile, URL, and masked token |
| `set-url ` | Set URL for the active `sp` profile |
| `set-profile ` | Switch the active `sp` profile |
## Examples
```bash
sp config init
sp config show --json
sp config set-url http://127.0.0.1:8090
sp config set-profile staging
```
### File layout
`sp` uses the global XDG namespace `softprobe`:
```text
${XDG_CONFIG_HOME:-~/.config}/softprobe/config.jsonc # shared Softprobe connectivity config
${XDG_CONFIG_HOME:-~/.config}/softprobe/sp.jsonc # sp CLI-specific config
${XDG_CONFIG_HOME:-~/.config}/softprobe/spcode.jsonc # spcode AI assistant-specific config
.softprobe/ # local project config directory
${XDG_CACHE_HOME:-~/.cache}/softprobe/ # caches
${XDG_DATA_HOME:-~/.local/share}/softprobe/ # durable data, agent jars
${XDG_STATE_HOME:-~/.local/state}/softprobe/ # logs and state
```
`config.jsonc` is shared with other Softprobe tools. `sp.jsonc` is only for this CLI and overrides shared values. `spcode.jsonc` (global and inside project-level `.softprobe/`) is parsed separately by the `spcode` AI assistant engine.
### JSON output (`show`)
```json
{
"ok": true,
"command": "config show",
"data": {
"profile": "default",
"url": "http://127.0.0.1:8090",
"tokenConfigured": true,
"sources": [
"~/.config/softprobe/config.jsonc",
"~/.config/softprobe/sp.jsonc"
]
}
}
```
## Precedence
Later sources override earlier sources:
1. Defaults.
2. `${XDG_CONFIG_HOME}/softprobe/config.jsonc`.
3. `${XDG_CONFIG_HOME}/softprobe/sp.jsonc`.
4. Extra config from `SP_CONFIG`, when set.
5. Extra config from `--config`, when set.
6. Selected profile. Profile selection priority is `--profile`, `SP_PROFILE`,
merged `profile`, then `default`.
7. Scalar env overrides: `SP_API_URL`, `SP_TOKEN`, `SP_AGENT_JAR`.
8. Scalar CLI flags: `--api-url`, `--token`, `--agent-jar`.
Explicit unknown profiles fail closed with `PROFILE_NOT_FOUND`; they never
silently fall back to `default`.
## REST mapping
Config subcommands are **local only** (no HTTP), except `config agent load` in [config legacy](./config-legacy).
## Errors
| Code | Exit | Cause |
|------|------|-------|
| `CONFIG_MISSING` | 2 | No config file; run `init` |
| `PROFILE_NOT_FOUND` | 2 | Unknown profile name |
| `CONFIG_PARSE_ERROR` | 2 | Invalid JSONC |
| `CONFIG_WRITE_ERROR` | 2 | Config file could not be written |
## Related
* [Configuration guide](/en/testing/installation/configuration)
* [auth](./auth)
---
---
url: /zh/testing/commands/config.md
---
# sp config
**When agents use this:** Once per session to verify connectivity; CI uses env vars instead of `init`.
## Synopsis
Manage XDG-backed SoftProbe config, profiles, and backend URL.
## Subcommands
| Subcommand | Description |
|------------|-------------|
| `init` | Create `${XDG_CONFIG_HOME}/softprobe/config.jsonc` and `sp.jsonc` |
| `show` | Print resolved config sources, active profile, URL, and masked token |
| `set-url ` | Set URL for the active `sp` profile |
| `set-profile ` | Switch the active `sp` profile |
## Examples
```bash
sp config init
sp config show --json
sp config set-url http://127.0.0.1:8090
sp config set-profile staging
```
### File layout
`sp` uses the global XDG namespace `softprobe`:
```text
${XDG_CONFIG_HOME:-~/.config}/softprobe/config.jsonc # shared Softprobe connectivity config
${XDG_CONFIG_HOME:-~/.config}/softprobe/sp.jsonc # sp CLI-specific config
${XDG_CONFIG_HOME:-~/.config}/softprobe/spcode.jsonc # spcode AI assistant-specific config
.softprobe/ # local project config directory
${XDG_CACHE_HOME:-~/.cache}/softprobe/ # caches
${XDG_DATA_HOME:-~/.local/share}/softprobe/ # durable data, agent jars
${XDG_STATE_HOME:-~/.local/state}/softprobe/ # logs and state
```
`config.jsonc` is shared with other Softprobe tools. `sp.jsonc` is only for this CLI and overrides shared values. `spcode.jsonc` (global and inside project-level `.softprobe/`) is parsed separately by the `spcode` AI assistant engine.
### JSON output (`show`)
```json
{
"ok": true,
"command": "config show",
"data": {
"profile": "default",
"url": "http://127.0.0.1:8090",
"tokenConfigured": true,
"sources": [
"~/.config/softprobe/config.jsonc",
"~/.config/softprobe/sp.jsonc"
]
}
}
```
## Precedence
Later sources override earlier sources:
1. Defaults.
2. `${XDG_CONFIG_HOME}/softprobe/config.jsonc`.
3. `${XDG_CONFIG_HOME}/softprobe/sp.jsonc`.
4. Extra config from `SP_CONFIG`, when set.
5. Extra config from `--config`, when set.
6. Selected profile. Profile selection priority is `--profile`, `SP_PROFILE`,
merged `profile`, then `default`.
7. Scalar env overrides: `SP_API_URL`, `SP_TOKEN`, `SP_AGENT_JAR`.
8. Scalar CLI flags: `--api-url`, `--token`, `--agent-jar`.
Explicit unknown profiles fail closed with `PROFILE_NOT_FOUND`; they never
silently fall back to `default`.
## REST mapping
Config subcommands are **local only** (no HTTP), except `config agent load` in [config legacy](./config-legacy).
## Errors
| Code | Exit | Cause |
|------|------|-------|
| `CONFIG_MISSING` | 2 | No config file; run `init` |
| `PROFILE_NOT_FOUND` | 2 | Unknown profile name |
| `CONFIG_PARSE_ERROR` | 2 | Invalid JSONC |
| `CONFIG_WRITE_ERROR` | 2 | Config file could not be written |
## Related
* [Configuration guide](/zh/testing/installation/configuration)
* [auth](./auth)
---
---
url: /en/testing/commands/config-legacy.md
---
# sp config legacy
**When agents use this:** Only when migrating old Mongo-backed config — prefer `sp policy` for recording/mock/compare.
## Synopsis
CRUD wrappers for `/api/config/*` abstract configurable controllers.
## Subcommand groups
| Group | Base path |
|-------|-----------|
| `legacy application` | `/api/config/application` |
| `legacy schedule` | `/api/config/schedule` |
| `legacy operation` | `/api/config/applicationOperation` |
| `legacy dynamic-class` | `/api/config/dynamicClass` |
| `legacy comparison` | `/api/config/comparison/*` |
| `legacy multiservice` | `/api/config/multiservice` |
| `legacy yaml-template` | `/api/config/yamlTemplate` |
Common pattern per resource:
```text
GET …/useResult/appId/{appId}
GET …/useResultAsList/appId/{appId}
POST …/modify/{modifyType}
POST …/batchModify/{modifyType}
```
## Agent guidance
* **Do not** use legacy schedule operation include/exclude as source of truth — recording policy overlay applies at read time.
* Use `sp policy recording|mock|compare` for declarative config.
## `sp config agent`
| Subcommand | Method | Path |
|------------|--------|------|
| `agent load --app ` | POST | `/api/config/agent/load` |
| `agent status` | POST | `/api/config/agent/agentStatus` |
Replaces `sp_api` `app_config`.
## Related
* [policy](/en/testing/commands/policy)
* [concepts](/en/testing/agents/concepts)
---
---
url: /zh/testing/commands/config-legacy.md
---
# sp config legacy
**When agents use this:** Only when migrating old Mongo-backed config — prefer `sp policy` for recording/mock/compare.
## Synopsis
CRUD wrappers for `/api/config/*` abstract configurable controllers.
## Subcommand groups
| Group | Base path |
|-------|-----------|
| `legacy application` | `/api/config/application` |
| `legacy schedule` | `/api/config/schedule` |
| `legacy operation` | `/api/config/applicationOperation` |
| `legacy dynamic-class` | `/api/config/dynamicClass` |
| `legacy comparison` | `/api/config/comparison/*` |
| `legacy multiservice` | `/api/config/multiservice` |
| `legacy yaml-template` | `/api/config/yamlTemplate` |
Common pattern per resource:
```text
GET …/useResult/appId/{appId}
GET …/useResultAsList/appId/{appId}
POST …/modify/{modifyType}
POST …/batchModify/{modifyType}
```
## Agent guidance
* **Do not** use legacy schedule operation include/exclude as source of truth — recording policy overlay applies at read time.
* Use `sp policy recording|mock|compare` for declarative config.
## `sp config agent`
| Subcommand | Method | Path |
|------------|--------|------|
| `agent load --app ` | POST | `/api/config/agent/load` |
| `agent status` | POST | `/api/config/agent/agentStatus` |
Replaces `sp_api` `app_config`.
## Related
* [policy](/zh/testing/commands/policy)
* [concepts](/zh/testing/agents/concepts)
---
---
url: /en/testing/commands/diagnose.md
---
# sp diagnose
**When agents use this:** One-shot workflows that combine progress, report, storage, and trace APIs — fewer manual steps than chaining low-level commands.
## Synopsis
| Subcommand | Description |
|------------|-------------|
| `replay ` | Plan progress, failed cases, diff artifacts on disk |
| `trace ` | Record trace, completeness, summary artifacts |
## `diagnose replay`
Replaces the manual sequence in [Diagnose replay failure](/en/testing/examples/agent-diagnose-replay):
```bash
sp diagnose replay plan-abc123 --failed-only --out-dir .sp-work --json
```
| Flag | Default | Description |
|------|---------|-------------|
| `--failed-only` | `true` | Filter to cases with compare failures |
| `--out-dir` | `.sp-work` | Write `{planId}/{planItemId}-diff.json` files |
| `--page` / `--limit` | global | Pagination for case query |
Steps performed:
1. `GET /api/progress?planId=…`
2. `POST /api/report/queryReplayCase` with `diffResultCode=1` when `--failed-only`
3. For each failed case with `diffId`: `GET /api/report/queryDiffMsgById/{id}` → artifact file
Example `data` shape:
```json
{
"planId": "plan-abc123",
"status": "FINISHED",
"classification": "invalid_target",
"message": "Connection refused: travel-ota:9999",
"failedCaseCount": 0,
"invalidCaseCount": 12,
"artifacts": [
".sp-work/plan-abc123/item-1-diff.json"
]
}
```
`classification` is one of: `empty_window`, `invalid_target`, `assertion_failure`, `mixed`, `other`. `message` comes from backend `errorMessage` or case send errors when available — not fabricated client copy.
**Note:** `nextActions` was removed from `diagnose replay --json` output (feature 007). Use `classification` + `message` for automation.
## `diagnose trace`
```bash
sp diagnose trace 4bf92f3577b34da6a3ce929d0e0e4736 --out-dir .sp-work --json
```
Fetches:
* `GET /api/storage/record/trace/{traceId}`
* `GET /api/storage/record/completeness?traceId=…`
* Trace summary (when available)
Writes JSON under `{outDir}/trace-{traceId}/` and returns a summary plus `nextActions` (e.g. `sp record query --trace-id …`).
## After diagnose: unified logs
`diagnose replay` returns diff artifacts but not runtime log lines. For each failed case:
1. Copy **`traceId`** from `sp replay case list --plan --failed --json` (v1 log lookup key — not `replayId`).
2. Query unified logs:
```bash
curl -s "${SP_API_URL}/api/recorder/logs?trace_id=${TRACE_ID}&since=${SINCE}&until=${UNTIL}" \
-H "Accept: application/json" -o .sp-work/unified-logs.json
jq '.rows | length' .sp-work/unified-logs.json
jq '[.rows[].source] | group_by(.) | map({source: .[0], n: length})' .sp-work/unified-logs.json
```
3. Triage: empty rows + `warnings` → reader/schema issue; empty + no warnings → wrong window or ingest lag; rows from `agent`, `app`, and `backend` → pipeline OK, read `body` and diff artifacts together.
On **`make e2e`** failures, pytest prints **Softprobe correlation** (`trace_id`) and **Unified logs** summaries — use those before widening the investigation.
See [Log correlation IDs](/en/testing/reference/log-correlation-ids) and [sp logs — troubleshooting](./logs#troubleshooting-failed-replays).
## Related
* [Log correlation IDs](/en/testing/reference/log-correlation-ids)
* [replay](./replay)
* [replay diff](./replay-diff)
* [record](./record)
* [trace](./trace)
---
---
url: /zh/testing/commands/diagnose.md
---
# sp diagnose
**When agents use this:** One-shot workflows that combine progress, report, storage, and trace APIs — fewer manual steps than chaining low-level commands.
## Synopsis
| Subcommand | Description |
|------------|-------------|
| `replay ` | Plan progress, failed cases, diff artifacts on disk |
| `trace ` | Record trace, completeness, summary artifacts |
## `diagnose replay`
Replaces the manual sequence in [Diagnose replay failure](/zh/testing/examples/agent-diagnose-replay):
```bash
sp diagnose replay plan-abc123 --failed-only --out-dir .sp-work --json
```
| Flag | Default | Description |
|------|---------|-------------|
| `--failed-only` | `true` | Filter to cases with compare failures |
| `--out-dir` | `.sp-work` | Write `{planId}/{planItemId}-diff.json` files |
| `--page` / `--limit` | global | Pagination for case query |
Steps performed:
1. `GET /api/progress?planId=…`
2. `POST /api/report/queryReplayCase` with `diffResultCode=1` when `--failed-only`
3. For each failed case with `diffId`: `GET /api/report/queryDiffMsgById/{id}` → artifact file
Example `data` shape:
```json
{
"planId": "plan-abc123",
"status": "FINISHED",
"classification": "invalid_target",
"message": "Connection refused: travel-ota:9999",
"failedCaseCount": 0,
"invalidCaseCount": 12,
"artifacts": [
".sp-work/plan-abc123/item-1-diff.json"
]
}
```
`classification` 取值之一:`empty_window`、`invalid_target`、`assertion_failure`、`mixed`、`other`。`message` 在可用时来自后端 `errorMessage` 或 case 发送错误——不是客户端伪造的固定文案。
**注意:** `nextActions` 已从 `diagnose replay --json` 输出中移除(feature 007)。请改用 `classification` + `message` 做自动化。
## `diagnose trace`
```bash
sp diagnose trace 4bf92f3577b34da6a3ce929d0e0e4736 --out-dir .sp-work --json
```
Fetches:
* `GET /api/storage/record/trace/{traceId}`
* `GET /api/storage/record/completeness?traceId=…`
* Trace summary (when available)
Writes JSON under `{outDir}/trace-{traceId}/` and returns a summary plus `nextActions` (e.g. `sp record query --trace-id …`).
## diagnose 之后:统一日志
`diagnose replay` 返回 diff 产物,但不包含运行时日志行。对每个失败的 case:
1. 从 `sp replay case list --plan --failed --json` 复制 **`traceId`**(v1 日志查询键——不是 `replayId`)。
2. 查询统一日志:
```bash
curl -s "${SP_API_URL}/api/recorder/logs?trace_id=${TRACE_ID}&since=${SINCE}&until=${UNTIL}" \
-H "Accept: application/json" -o .sp-work/unified-logs.json
jq '.rows | length' .sp-work/unified-logs.json
jq '[.rows[].source] | group_by(.) | map({source: .[0], n: length})' .sp-work/unified-logs.json
```
3. 分诊:rows 为空 + 有 `warnings` → reader/schema 问题;为空 + 无 warnings → 时间窗错误或摄入延迟;来自 `agent`、`app`、`backend` 的 rows → 管道正常,结合 `body` 与 diff 产物一起阅读。
在 **`make e2e`** 失败时,pytest 会打印 **Softprobe correlation**(`trace_id`)和 **Unified logs** 摘要——先看这些,再扩大排查范围。
参见 [日志关联 ID](/zh/testing/reference/log-correlation-ids) 和 [sp logs — 排查回放失败](./logs#troubleshooting-failed-replays)。
## Related
* [日志关联 ID](/zh/testing/reference/log-correlation-ids)
* [replay](./replay)
* [replay diff](./replay-diff)
* [record](./record)
* [trace](./trace)
---
---
url: /en/testing/commands/extraction-rule.md
---
# sp extraction-rule
**When agents use this:** Configure or preview business-attribute extraction for `sp trace find`.
## Subcommands
| Subcommand | Description |
|------------|-------------|
| `list --app ` | App extraction rules |
| `apply -f rules.yaml` | PUT app rules |
| `preview -f rules.yaml` | POST preview |
## REST mapping
| Subcommand | Method | Path |
|------------|--------|------|
| `list` | GET | `/api/applications/{appId}/extraction-rules` |
| `apply` | PUT | `/api/applications/{appId}/extraction-rules` |
| `preview` | POST | `/api/extraction-rules/preview` |
| agent pull | GET | `/api/agent/extraction-rules` (agent use; CLI rarely) |
## Related
* [trace](./trace)
---
---
url: /zh/testing/commands/extraction-rule.md
---
# sp extraction-rule
**When agents use this:** Configure or preview business-attribute extraction for `sp trace find`.
## Subcommands
| Subcommand | Description |
|------------|-------------|
| `list --app ` | App extraction rules |
| `apply -f rules.yaml` | PUT app rules |
| `preview -f rules.yaml` | POST preview |
## REST mapping
| Subcommand | Method | Path |
|------------|--------|------|
| `list` | GET | `/api/applications/{appId}/extraction-rules` |
| `apply` | PUT | `/api/applications/{appId}/extraction-rules` |
| `preview` | POST | `/api/extraction-rules/preview` |
| agent pull | GET | `/api/agent/extraction-rules` (agent use; CLI rarely) |
## Related
* [trace](./trace)
---
---
url: /en/testing/commands/group.md
---
# sp group & grant
**When agents use this:** Admin automation for access control (not typical diagnosis flows).
## Subcommands
### `sp group`
| Subcommand | Path prefix |
|------------|-------------|
| `list` | `/api/userGroup/list` |
| `my` | `/api/userGroup/my` |
### `sp grant`
| Subcommand | Path |
|------------|------|
| `list` | `GET /api/appGrant/list` |
`sp grant list` requires `--app `.
All commands require `access-token`.
## Examples
```bash
sp group list --json
sp group my --json
sp grant list --app my-app --json
```
## Related
* [app](/en/testing/commands/app)
---
---
url: /zh/testing/commands/group.md
---
# sp group & grant
**When agents use this:** Admin automation for access control (not typical diagnosis flows).
## Subcommands
### `sp group`
| Subcommand | Path prefix |
|------------|-------------|
| `list` | `/api/userGroup/list` |
| `my` | `/api/userGroup/my` |
### `sp grant`
| Subcommand | Path |
|------------|------|
| `list` | `GET /api/appGrant/list` |
`sp grant list` requires `--app `.
All commands require `access-token`.
## Examples
```bash
sp group list --json
sp group my --json
sp grant list --app my-app --json
```
## Related
* [app](/zh/testing/commands/app)
---
---
url: /en/testing/commands/health.md
---
# sp health
**When agents use this:** Preflight before a long diagnosis session; CI smoke check.
## Synopsis
Check sp-backend availability.
## Usage
```bash
sp health --json
sp health --api-url https://sp.example.com --json
```
## JSON output
```json
{
"ok": true,
"command": "health",
"data": {
"status": "UP",
"url": "http://127.0.0.1:8090"
}
}
```
## REST mapping
| Method | Path |
|--------|------|
| GET | `/vi/health` |
No authentication required in default deployments (verify for your environment).
## Errors
| Situation | Exit |
|-----------|------|
| Connection refused | 1 |
| Non-200 HTTP | 1 |
## Related
* [Installation](/en/testing/installation/)
---
---
url: /zh/testing/commands/health.md
---
# sp health
**When agents use this:** Preflight before a long diagnosis session; CI smoke check.
## Synopsis
Check sp-backend availability.
## Usage
```bash
sp health --json
sp health --api-url https://sp.example.com --json
```
## JSON output
```json
{
"ok": true,
"command": "health",
"data": {
"status": "UP",
"url": "http://127.0.0.1:8090"
}
}
```
## REST mapping
| Method | Path |
|--------|------|
| GET | `/vi/health` |
No authentication required in default deployments (verify for your environment).
## Errors
| Situation | Exit |
|-----------|------|
| Connection refused | 1 |
| Non-200 HTTP | 1 |
## Related
* [Installation](/zh/testing/installation/)
---
---
url: /en/testing/commands/logs.md
---
# sp logs
**When agents use this:** Retrieve correlated application, agent, and sp-backend logs for a W3C trace within caller-provided time bounds — without direct access to Parquet files or storage credentials.
**Prerequisite:** Unified log pipeline enabled (Vector ingest + Parquet storage + query wiring). See [Install sp-backend (server) — unified log pipeline](/en/testing/installation/server#unified-log-pipeline) and [Log correlation IDs](/en/testing/reference/log-correlation-ids).
v1 is **trace-id-only, canned lookup** — no SQL, no ad hoc query language, no `sp logs status` health command, and no replay/plan lookup keys.
**API:** `GET /api/recorder/logs?trace_id=…&since=…&until=…` on sp-backend. Top-level **`sp logs`** uses the same contract.
***
## Synopsis
Query unified log rows by **`trace_id`** within a caller-provided time window.
```bash
sp logs --trace-id --since