# Tianji Documentation Export (Full)

This file aggregates documentation for LLM consumption.
It preserves Markdown headings, links, and code fences; frontmatter and MDX ESM are removed.

================================================================================

===== FILE: docs/ai-router/intro.md =====

# AI Router

AI Router gives one stable AI endpoint for a group of AI Gateways. It routes each request through configured gateway routes, spreads traffic by weight inside the same tier, and falls back to the next tier when retryable failures happen.

Use it when you want:

- One endpoint for your application instead of hard-coding one AI provider.
- Weighted traffic split across multiple gateways.
- Fallback from a primary provider to backup providers during outages or rate limits.
- A migration path where you can move traffic gradually by changing weights.

## How it relates to AI Gateway

AI Gateway is still the unit that stores provider credentials, custom base URLs, model pricing, quota alerts, and gateway logs. AI Router does not replace that.

AI Router only decides which gateway route should receive the request.

The runtime flow is:

1. Your application calls an AI Router endpoint.
2. AI Router finds the router by workspace ID and router ID.
3. AI Router chooses an eligible gateway route from the first tier.
4. The selected AI Gateway sends the request to the upstream AI provider.
5. If the attempt succeeds, AI Router returns that response.
6. If the attempt fails with a retryable error, AI Router tries another route in the same tier, then the next tier.

## Prerequisites

Before adding routes, create at least one AI Gateway with a stored model API key. Gateways without a stored key are not shown in the AI Router route picker.

Runtime requests still need a Tianji API key:

- For OpenAI-compatible endpoints, send `Authorization: Bearer <YOUR_TIANJI_API_KEY>`.
- For Anthropic Messages endpoints, send `x-api-key: <YOUR_TIANJI_API_KEY>`.

Tianji verifies the caller API key, then uses the stored AI Gateway provider key for the upstream request.

## Create a router

1. Open **AI Router** in the Tianji sidebar.
2. Click **Add AI Router**.
3. Enter a router name.
4. Keep **Enabled** on if the router should accept runtime traffic.
5. Save the router.

After the router is created, open the **Routes** tab to configure tiers and gateway routes.

## Tiers

A tier is a fallback level.

Requests always start at the first tier. If a retryable failure happens, AI Router keeps trying eligible routes in that tier. If every eligible route in the tier fails, AI Router moves to the next tier.

Use multiple tiers when you want strict fallback order.

Example:

| Tier | Routes | Meaning |
| --- | --- | --- |
| Tier 1 | OpenAI primary, OpenRouter primary | Normal production traffic |
| Tier 2 | DeepSeek backup | Backup after primary providers fail |
| Tier 3 | Custom internal model | Last-resort fallback |

Drag tiers to reorder them. The top tier is tried first.

## Weights inside a tier

Routes inside the same tier do not have a fixed order. They share traffic by weight.

Example:

| Route | Weight | Approximate first-attempt share |
| --- | ---: | ---: |
| Gateway A | 80 | 80% |
| Gateway B | 20 | 20% |

This is useful for:

- Random traffic split across providers.
- Gradual migration from one provider to another.
- Canarying a new gateway with a small percentage of traffic.

If you need strict order, put the routes in different tiers instead of the same tier.

## Add a gateway route

On the **Routes** tab:

1. Click **Add Gateway** inside a tier.
2. Select an existing AI Gateway.
3. Select the provider mode for this route.
4. Set the route options.
5. Save.

You can edit or delete a route later from the route card.

### Provider

Provider controls how AI Router calls the selected AI Gateway for this route. The same AI Gateway can be used in different routes with different provider modes if that matches your setup.

Supported provider values:

- `openai`
- `deepseek`
- `anthropic`
- `openrouter`
- `custom`

For `custom`, AI Router uses the custom model settings stored on the selected AI Gateway, such as custom base URL and custom model name.

### Weight

Weight controls how traffic is distributed between routes in the same tier. Higher weight means the route is more likely to be tried first.

Default: `100`.

### Model Override

Model Override is optional.

When set, AI Router replaces the request `model` with this value before sending the request to the selected gateway route. Leave it empty if the application request should decide the model.

### Timeout

Timeout is the maximum time for one gateway attempt.

Default: `30000ms`.

If the attempt times out, AI Router treats it as retryable and can try the next eligible route.

### Retryable Status Codes

AI Router always treats network errors, timeouts, and these status codes as retryable:

- `429`
- `500`
- `502`
- `503`
- `504`

Use **Retryable Status Codes** to add more status codes for a route. For example, you can add `408` if a provider often reports request timeout as an HTTP response.

Be careful with validation errors such as `400` or `401`. Those usually mean the request or key is wrong, and retrying another provider can hide the real problem.

## Logs

The **Logs** tab shows runtime attempts for a router:

- Status: `Success`, `Failed`, or `Partial`.
- Protocol: the matched request protocol.
- Attempts: how many gateway routes were tried.
- Final Gateway: the gateway that produced the final result.
- Final Gateway Log: the linked AI Gateway log ID.
- Duration.

Use router logs to understand failover behavior. Use the linked AI Gateway logs to inspect token usage, upstream model details, cost, and provider response data.


===== FILE: docs/ai-router/usage.md =====

# AI Router usage

AI Router exposes OpenAI-compatible and Anthropic-compatible endpoints under your Tianji server:

```bash
https://your-tianji-domain.com/api/ai-router/<workspaceId>/<routerId>/<provider>/v1/...
```

The `<provider>` segment must match the provider mode you configured on the route.

## Supported endpoints

| Provider segment | Chat Completions | Responses | Anthropic Messages |
| --- | --- | --- | --- |
| `openai` | `/openai/v1/chat/completions` | `/openai/v1/responses` | - |
| `deepseek` | `/deepseek/v1/chat/completions` | - | - |
| `anthropic` | `/anthropic/v1/chat/completions` | - | `/anthropic/v1/messages` |
| `openrouter` | `/openrouter/v1/chat/completions` | - | `/openrouter/v1/messages` |
| `custom` | `/custom/v1/chat/completions` | `/custom/v1/responses` | `/custom/v1/messages` |

## OpenAI Chat Completions

Base URL for OpenAI SDK:

```bash
https://your-tianji-domain.com/api/ai-router/<workspaceId>/<routerId>/openai/v1
```

Node.js:

```js

const client = new OpenAI({
  apiKey: process.env.TIANJI_API_KEY,
  baseURL:
    'https://your-tianji-domain.com/api/ai-router/<workspaceId>/<routerId>/openai/v1',
});

const response = await client.chat.completions.create({
  model: 'gpt-5.5',
  messages: [{ role: 'user', content: 'Hello from Tianji AI Router' }],
});

console.log(response.choices[0].message);
```

cURL:

```bash
curl -X POST 'https://your-tianji-domain.com/api/ai-router/<workspaceId>/<routerId>/openai/v1/chat/completions' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <YOUR_TIANJI_API_KEY>' \
  -d '{
    "model": "gpt-5.5",
    "messages": [
      {
        "role": "user",
        "content": "Hello from Tianji AI Router"
      }
    ]
  }'
```

## OpenAI Responses

Base URL for OpenAI SDK:

```bash
https://your-tianji-domain.com/api/ai-router/<workspaceId>/<routerId>/openai/v1
```

Node.js:

```js

const client = new OpenAI({
  apiKey: process.env.TIANJI_API_KEY,
  baseURL:
    'https://your-tianji-domain.com/api/ai-router/<workspaceId>/<routerId>/openai/v1',
});

const response = await client.responses.create({
  model: 'gpt-5.5',
  input: 'Write a short deployment checklist.',
});

console.log(response.output_text);
```

cURL:

```bash
curl -X POST 'https://your-tianji-domain.com/api/ai-router/<workspaceId>/<routerId>/openai/v1/responses' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <YOUR_TIANJI_API_KEY>' \
  -d '{
    "model": "gpt-5.5",
    "input": "Write a short deployment checklist."
  }'
```

## Anthropic Messages

Base URL for Anthropic SDK:

```bash
https://your-tianji-domain.com/api/ai-router/<workspaceId>/<routerId>/anthropic
```

Node.js:

```js

const client = new Anthropic({
  apiKey: process.env.TIANJI_API_KEY,
  baseURL:
    'https://your-tianji-domain.com/api/ai-router/<workspaceId>/<routerId>/anthropic',
});

const message = await client.messages.create({
  model: 'claude-opus-4-8',
  max_tokens: 1024,
  messages: [{ role: 'user', content: 'Hello from Tianji AI Router' }],
});

console.log(message.content);
```

cURL:

```bash
curl -X POST 'https://your-tianji-domain.com/api/ai-router/<workspaceId>/<routerId>/anthropic/v1/messages' \
  -H 'Content-Type: application/json' \
  -H 'x-api-key: <YOUR_TIANJI_API_KEY>' \
  -H 'anthropic-version: 2023-06-01' \
  -d '{
    "model": "claude-opus-4-8",
    "max_tokens": 1024,
    "messages": [
      {
        "role": "user",
        "content": "Hello from Tianji AI Router"
      }
    ]
  }'
```

## Custom provider routes

Use the `custom` provider segment when the selected AI Gateway stores a custom base URL or custom model name.

Examples:

```bash
/api/ai-router/<workspaceId>/<routerId>/custom/v1/chat/completions
/api/ai-router/<workspaceId>/<routerId>/custom/v1/responses
/api/ai-router/<workspaceId>/<routerId>/custom/v1/messages
```

The custom upstream details stay on AI Gateway. AI Router only selects the route and forwards the normalized request.

## Recommended routing patterns

### Active backup

Use this when uptime matters more than traffic distribution.

| Tier | Route | Weight |
| --- | --- | ---: |
| 1 | Primary OpenAI gateway | 100 |
| 2 | OpenRouter gateway | 100 |
| 3 | Custom fallback gateway | 100 |

AI Router tries Tier 2 only after Tier 1 returns retryable failures.

### Weighted split

Use this when you want to share traffic across providers in normal operation.

| Tier | Route | Weight |
| --- | --- | ---: |
| 1 | Gateway A | 80 |
| 1 | Gateway B | 20 |

Both routes are in the same tier, so there is no primary/secondary order. The weights decide which route is likely to be tried first.

### Canary migration

Use this when testing a new provider.

| Tier | Route | Weight |
| --- | --- | ---: |
| 1 | Current provider | 95 |
| 1 | New provider | 5 |
| 2 | Stable fallback | 100 |

Increase the new provider weight after you confirm quality and reliability in the logs.

## Troubleshooting

### No eligible AI Router nodes are available

Check that:

- The router is enabled.
- At least one tier has enabled routes.
- The selected AI Gateway still has a stored model API key.
- The route provider supports the endpoint you are calling.

### The router stops after one failed attempt

AI Router only continues after retryable failures.

Network errors, timeouts, `429`, `500`, `502`, `503`, and `504` are retryable by default. Add route-specific retryable status codes if a provider uses other temporary failure codes.

### The wrong model is being used

Check both places:

- Route **Model Override**. If set, it replaces the request `model`.
- AI Gateway custom model name. For `custom` routes, the gateway can replace the model with its custom model name.

### The request returns 401 or 403

Use a Tianji API key in the runtime request. Do not send the upstream provider key to AI Router when the gateway stores its own provider credential.

For OpenAI-compatible endpoints, use:

```bash
Authorization: Bearer <YOUR_TIANJI_API_KEY>
```

For Anthropic Messages endpoints, use:

```bash
x-api-key: <YOUR_TIANJI_API_KEY>
```


===== FILE: docs/api/authentication.md =====

# Authentication

This document provides detailed instructions on how to authenticate with the Tianji API, including obtaining, using, and managing API keys.

## Authentication Method

The Tianji API uses **Bearer Token** authentication. You need to include your API key in the HTTP header of each API request.

### HTTP Header Format

```http
Authorization: Bearer <YOUR_API_KEY>
```

## Obtaining API Keys

1. Log in to your Tianji instance
2. Click on your avatar in the top right corner
4. Find the **API Keys** section
5. Click the + button to create a new API key
6. Name your API key and save it

## API Key Management

### View Existing Keys

In the **API Keys** section, you can see:
- API key name/description
- Creation date
- Last used time
- Usage count statistics

### Delete API Keys

If you need to revoke an API key:
1. Find the API key you want to delete
2. Click the **Delete** button
3. Confirm the deletion operation

:::warning Note
After deleting an API key, all applications using that key will no longer be able to access the API.
:::

## Using API Keys

### cURL Example

```bash
curl -X GET "https://your-tianji-domain.com/open/global/config" \
  -H "Authorization: Bearer <your_api_key_here>" \
  -H "Content-Type: application/json"
```

### JavaScript/Node.js Example

```javascript
const apiKey = '<your_api_key_here>';
const baseUrl = 'https://your-tianji-domain.com/open';

const headers = {
  'Authorization': `Bearer ${apiKey}`,
  'Content-Type': 'application/json'
};

// Using fetch
const response = await fetch(`${baseUrl}/global/config`, {
  method: 'GET',
  headers: headers
});

// Using axios
const axios = require('axios');
const response = await axios.get(`${baseUrl}/global/config`, {
  headers: headers
});
```

### Python Example

```python

$baseUrl = 'https://your-tianji-domain.com/open';

$headers = [
    'Authorization: Bearer ' . $apiKey,
    'Content-Type: application/json'
];

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $baseUrl . '/global/config');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$data = json_decode($response, true);
?>
```

## Permissions and Scope

### API Key Permissions

API keys inherit all permissions of their creator, including:
- Access to all data in the user's workspaces
- Execute all operations the user has permission for
- Manage resources created by that user

### Workspace Access

API keys can only access workspaces that the user belongs to. If you need to access multiple workspaces, ensure your user account has appropriate permissions for those workspaces.

## Error Handling

### Common Authentication Errors

#### 401 Unauthorized

```json
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Authorization not provided"
  }
}
```

**Causes**:
- No Authorization header provided
- Incorrect API key format

#### 403 Forbidden

```json
{
  "error": {
    "code": "FORBIDDEN", 
    "message": "Insufficient access"
  }
}
```

**Causes**:
- Invalid or deleted API key
- User doesn't have permission to access the requested resource

### Debugging Authentication Issues

1. **Check API key format**: Ensure you're using the `Bearer token_here` format
2. **Verify key validity**: Confirm the key still exists in the Tianji interface
3. **Check permissions**: Ensure the user account has permission to access the target resource
4. **Test simple endpoints**: Start by testing public endpoints like `/global/config`


===== FILE: docs/api/getting-started.md =====

# API Getting Started

Tianji provides a complete REST API that allows you to programmatically access and operate all Tianji features. This guide will help you quickly get started with the Tianji API.

## Overview

The Tianji API is based on REST architecture and uses JSON format for data exchange. All API requests must be made over HTTPS and require proper authentication.

### API Base URL

```bash
https://your-tianji-domain.com/open
```

### Supported Features

Through the Tianji API, you can:

- Manage website analytics data
- Create and manage monitoring projects
- Get server status information
- Manage surveys
- Operate telemetry data
- Create and manage workspaces

## Quick Start

### 1. Get API Key

Before using the API, you need to obtain an API key:

1. Log in to your Tianji instance
2. Click on your avatar in the top right corner
4. Find the **API Keys** section
5. Click the + button to create a new API key
6. Name your API key and save it

### 2. Enable OpenAPI

Make sure your Tianji instance has OpenAPI access enabled:

Set in your environment variables:
```bash
ALLOW_OPENAPI=true
```

### 3. First API Call

Test your API connection using curl:

```bash
curl -X GET "https://your-tianji-domain.com/open/global/config" \
  -H "Authorization: Bearer <YOUR_API_KEY>" \
  -H "Content-Type: application/json"
```

## Next Steps

- Check the [Authentication Documentation](./authentication.md) for detailed authentication methods
- Browse the [API Reference Documentation](/api) for all available endpoints
- Use the [OpenAPI SDK](./openapi-sdk.md) for type-safe API calls


===== FILE: docs/api/openapi-sdk.md =====

# OpenAPI SDK Usage Guide

This document provides detailed instructions on how to use the Tianji SDK to call OpenAPI interfaces and achieve complete programmatic access to Tianji services.

## Overview

The Tianji OpenAPI SDK is based on an auto-generated TypeScript client, providing type-safe API calling methods. Through the SDK, you can:

- Manage workspaces and websites
- Retrieve analytics data and statistics
- Operate monitoring projects
- Manage surveys
- Handle Feed channels and events
- ...

[Complete API Documentation](/api)

## Installation and Initialization

### Install SDK

```bash
npm install tianji-client-sdk
# or
yarn add tianji-client-sdk
# or
pnpm add tianji-client-sdk
```

### Initialize OpenAPI Client

```javascript

initOpenapiSDK('https://your-tianji-domain.com', {
  apiKey: 'your-api-key'
});
```

## Global Configuration API

### Get System Configuration

```javascript

async function getSystemConfig() {
  try {
    const config = await openApiClient.GlobalService.globalConfig();
    
    console.log('Allow registration:', config.allowRegister);
    console.log('AI features enabled:', config.ai.enable);
    console.log('Billing enabled:', config.enableBilling);
    
    return config;
  } catch (error) {
    console.error('Failed to get system configuration:', error);
  }
}
```


===== FILE: docs/application/tracking.md =====

# Application Tracking

Tianji provides a powerful SDK for tracking events and user behavior in your applications. This guide explains how to integrate and use the Application Tracking SDK in your projects.

## Installation

Install the Tianji react native SDK in your project:

```bash
npm install tianji-react-native
# or
yarn add tianji-react-native
# or
pnpm add tianji-react-native
```

## Initialization

Before using any tracking features, you need to initialize the Application SDK with your Tianji server URL and application ID:

```ts

initApplication({
  serverUrl: 'https://tianji.example.com',  // Your Tianji server URL
  applicationId: 'your-application-id'       // Your application identifier
});
```

## Tracking Events

You can track custom events in your application to monitor user actions and behaviors:

```ts

// Track a simple event
reportApplicationEvent('Button Clicked');

// Track an event with additional data
reportApplicationEvent('Purchase Completed', {
  productId: 'product-123',
  price: 29.99,
  currency: 'USD'
});
```

## Screen Tracking

Track screen views in your application to understand user navigation patterns:

### Setting Current Screen

You can set the current screen information which will be included in subsequent events:

```ts

// Update current screen when user navigates
updateCurrentApplicationScreen('ProductDetails', { productId: 'product-123' });
```

### Reporting Screen Views

Explicitly report screen view events:

```ts

// Report current screen view
reportApplicationScreenView();

// Or report a specific screen view
reportApplicationScreenView('Checkout', { cartItems: 3 });
```

#### Intergate with expo-router

```tsx

// Identify a user with their information
identifyApplicationUser({
  id: 'user-123',          // Unique user identifier
  email: 'user@example.com',
  name: 'John Doe',
  // Add any other user properties
  plan: 'premium',
  signupDate: '2023-01-15'
});
```

## API Reference

### `initApplication(options)`

Initializes the application tracking SDK.

**Parameters:**

- `options`: ApplicationTrackingOptions
  - `serverUrl`: Your Tianji server URL (e.g., 'https://tianji.example.com')
  - `applicationId`: Your application identifier

### `reportApplicationEvent(eventName, eventData?, screenName?, screenParams?)`

Sends an application event to the Tianji server.

**Parameters:**

- `eventName`: Name of the event (max 50 chars)
- `eventData`: (Optional) Event data object
- `screenName`: (Optional) Screen name to override current screen
- `screenParams`: (Optional) Screen parameters to override current screen params

### `updateCurrentApplicationScreen(name, params)`

Updates the current application screen information.

**Parameters:**

- `name`: Screen name
- `params`: Screen parameters object

### `reportApplicationScreenView(screenName?, screenParams?)`

Sends a screen view event to the Tianji server.

**Parameters:**

- `screenName`: (Optional) Screen name to override current screen
- `screenParams`: (Optional) Screen parameters to override current screen params

### `identifyApplicationUser(userInfo)`

Identifies a user in the application.

**Parameters:**

- `userInfo`: User identification data object

## Payload Limitations

- Language information: max 35 characters
- Operating system information: max 20 characters
- URL information: max 500 characters
- Event name: max 50 characters


===== FILE: docs/dev/prepare.md =====

# Prepare To Develop

## Prepare Database which using docker

```bash
docker run --name tianji-pg -e POSTGRES_DB=tianji -e POSTGRES_USER=tianji -e POSTGRES_PASSWORD=tianji -d postgres:15.4-alpine
```

## Prepare .env file

```bash
cp .env.example src/server/.env
```

config `DATABASE_URL` with `postgresql://tianji:tianji@localhost:5432/tianji?schema=public`

Like this:

```ini
DATABASE_URL="postgresql://tianji:tianji@localhost:5432/tianji?schema=public"
ALLOW_OPENAPI=true
```

## Prepare Database struct

Now you can visit database, and you can visit 

```bash
cd src/server && pnpm db:migrate:apply
```

## Default Account

Default Account is `admin`/`admin`


===== FILE: docs/events/track.md =====

# Event Tracking

You can track user actions on your website. Tianji provides a simple way to do that.

## Using Script Tag

if you are using script tag, you just need to call `track` function in any where after you inject script

```ts
tianji.track(eventName, data);
```

## Using SDK

if you are using sdk, you just need to call `reportEvent()` function after you `initTianjiTracker()`

```ts

initTianjiTracker({
  url: backendUrl,
  websiteId,
});
    
reportEvent('Demo Event', {
  foo: 'bar',
});
```


===== FILE: docs/feed/channels.md =====

# Channels

Channels are containers of events. You can create multiple channels for different products/teams/environments.

## Create a channel

Console → Feed → Add Channel.

Fields

- name: Display name
- notifyFrequency: Control how often notifications are sent for this channel
- notificationIds: Select existing notification targets to receive fan-out

## Edit a channel

You can update name, notificationIds, notifyFrequency, and set an optional webhookSignature. Once a signature is set, any public webhook to this channel must include header `x-webhook-signature` with the same value.

## API

- List channels: GET `/open/workspace/{workspaceId}/feed/channels`
- Channel info: GET `/open/workspace/{workspaceId}/feed/{channelId}/info`
- Update: POST `/open/workspace/{workspaceId}/feed/{channelId}/update`
- Create: POST `/open/workspace/{workspaceId}/feed/createChannel`
- Delete: DELETE `/open/workspace/{workspaceId}/feed/{channelId}/del`

Example (update notification targets)

```bash
curl -X POST \
  "$BASE_URL/workspace/$WORKSPACE_ID/feed/$CHANNEL_ID/update" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Ops",
    "notifyFrequency": 60,
    "notificationIds": ["notif_123", "notif_456"]
  }'
```


===== FILE: docs/feed/integrations.md =====

# Integrations

Tianji provides built-in webhook adapters to convert third-party payloads into Feed events.

## GitHub

Endpoint

POST `/open/feed/{channelId}/github`

Notes

- Use "application/json" content type.
- Header `X-GitHub-Event` is required by GitHub and consumed by the adapter.
- Supported types: `push`, `star`, `issues` (opened/closed). Others will be logged as unknown.

## Stripe

Endpoint

POST `/open/feed/{channelId}/stripe`

Notes

- Configure a Stripe webhook endpoint pointing to the URL above.
- Supported types: `payment_intent.succeeded`, `payment_intent.canceled`, `customer.subscription.created`, `customer.subscription.deleted`.

## Sentry

Endpoint

POST `/open/feed/{channelId}/sentry`

Notes

- Header `Sentry-Hook-Resource: event_alert` and action `triggered` are mapped to Feed events.
- See step-by-step screenshots in "Integration with Sentry".

## Tencent Cloud Alarm

Endpoint

POST `/open/feed/{channelId}/tencent-cloud/alarm`

Notes

- Supports alarm types `event` and `metric`. Payload is validated; invalid requests are rejected.

## Webhook Playground

Endpoint

POST `/open/feed/playground/{workspaceId}`

Notes

- Echo headers/body/method/url to the workspace real-time playground for debugging integrations.


===== FILE: docs/feed/intro.md =====

# Feed overview

Feed is a lightweight event stream for your workspace. It helps teams aggregate important events from different systems into channels, collaborate around incidents, and keep stakeholders informed.

## Concepts

- Channel: A logical stream to collect and organize events. Each channel can be connected to one or more notification targets and can optionally require a webhook signature.
- Event: A single record with name, content, tags, source, sender identity, importance and optional payload. Events can be archived/unarchived.
- State: A special kind of ongoing event that can be upserted (created or updated) repeatedly by a stable eventId, and resolved when finished.
- Integration: Built-in webhook adapters that convert 3rd-party payloads (e.g. GitHub, Stripe, Sentry, Tencent Cloud Alarm) into Feed events.
- Notification: Channels can fan-out events to configured notifiers; delivery frequency can be tuned by channel settings.

## Typical use cases

- Product and infra incident stream across multiple services
- CI/CD deployment and release notices
- Billing and subscription signals
- Security, monitoring and error alerts


===== FILE: docs/feed/sentry.md =====

# Integration with Sentry

:::info
Learn more about sentry in [sentry.io](https://sentry.io/)
:::

Click `Settings` => `Integrations` => `Create New Integration`

![](/img/docs/sentry/sentry1.png)

Create a `Internal Integration` application

![](/img/docs/sentry/sentry2.png)

Input name `Tianji` and put in webhook url into form.

![](/img/docs/sentry/sentry3.png)

Don't forget enable `Alert Rule Action`

![](/img/docs/sentry/sentry4.png)

Then, add issue read `permission`, and webhook add `issue` and `error`

![](/img/docs/sentry/sentry5.png)

Finally, you can create a alert rule, and you can see `Tianji` in notification section dropdown list

![](/img/docs/sentry/sentry6.png)


===== FILE: docs/feed/state.md =====

# State (ongoing incidents)

Feed State models ongoing incidents with an immutable `eventId`. You can repeatedly upsert updates to the same `eventId` until it is resolved.

## Endpoints

- Upsert state (public): `POST /open/feed/{channelId}/state/upsert`
- Resolve state (auth): `POST /open/workspace/{workspaceId}/feed/{channelId}/state/resolve`
- List ongoing states (auth): `GET /open/workspace/{workspaceId}/feed/state/all?channelId=...&limit=...`

## Upsert body

```json
{
  "eventId": "deploy#2025-08-12",
  "eventName": "deploy_progress",
  "eventContent": "Rollout 60%",
  "tags": ["prod"],
  "source": "ci",
  "senderId": "runner-42",
  "senderName": "GitHub Actions",
  "important": true,
  "payload": {"stage": "canary"}
}
```

If the channel is configured with `webhookSignature`, you must include header `x-webhook-signature`.

## Resolve example

```bash
curl -X POST \
  "$BASE_URL/workspace/$WORKSPACE_ID/feed/$CHANNEL_ID/state/resolve" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"stateId": "STATE_ID"}'
```


===== FILE: docs/install/docker-monitoring.md =====

# Docker Container Monitoring Configuration

## Default Monitoring Behavior

When you install Tianji using Docker or Docker Compose, the system automatically enables built-in server monitoring functionality. By default:

- **Tianji automatically monitors its own container's** system resource usage
- Monitoring data includes: CPU usage, memory usage, disk usage, network traffic, etc.
- This data is automatically reported to the default workspace without additional configuration
- The container will appear as `tianji-container` in the monitoring dashboard

## Monitoring All Docker Services on the Host Machine

If you want Tianji to monitor all Docker containers and services running on the host machine, not just Tianji itself, you need to map the Docker Socket into the container.

### Configuration Method

Add the following volumes configuration to the `tianji` service section in your `docker-compose.yml` file:

```yaml
services:
  tianji:
    image: moonrailgun/tianji
    # ... other configurations ...
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    # ... other configurations ...
```

### Complete docker-compose.yml Example

```yaml
version: '3'
services:
  tianji:
    image: moonrailgun/tianji
    build:
      context: ./
      dockerfile: ./Dockerfile
    ports:
      - "12345:12345"
    environment:
      DATABASE_URL: postgresql://tianji:tianji@postgres:5432/tianji
      JWT_SECRET: replace-me-with-a-random-string
      ALLOW_REGISTER: "false"
      ALLOW_OPENAPI: "true"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock  # Add this line
    depends_on:
      - postgres
    restart: always
  postgres:
    # ... postgres configuration ...
```

### Using Docker Run Command

If you start Tianji using the `docker run` command, you can add the following parameter:

```bash
docker run -d \
  --name tianji \
  -p 12345:12345 \
  -v /var/run/docker.sock:/var/run/docker.sock \
  moonrailgun/tianji
```

## Effects After Configuration

After adding the Docker Socket mapping, Tianji will be able to:

- Monitor all Docker containers running on the host machine
- Obtain container resource usage information
- Display container status information
- Provide a more comprehensive system monitoring view


===== FILE: docs/install/environment.md =====

# Environment Variables

Tianji supports various environment variables to customize its behavior. You can configure these variables in your docker compose `env` field or through your deployment environment.

## Basic Configuration

| Variable | Description | Default | Example |
| --- | --- | --- | --- |
| `PORT` | Server port | `12345` | `3000` |
| `JWT_SECRET` | Secret for JWT tokens | Random Text | `your-secret-key` |
| `ALLOW_REGISTER` | Enable user registration | `false` | `true` |
| `ALLOW_OPENAPI` | Enable OpenAPI access | `true` | `false` |
| `WEBSITE_ID` | Website identifier | - | `your-website-id` |
| `DISABLE_AUTO_CLEAR` | Disable automatic data cleanup | `false` | `true` |
| `DISABLE_ACCESS_LOGS` | Disable access logs | `false` | `true` |
| `DB_DEBUG` | Enable database debugging | `false` | `true` |
| `ALPHA_MODE` | Enable alpha features | `false` | `true` |
| `ENABLE_FUNCTION_WORKER` | Enable function worker | `false` | `true` |
| `REGISTER_AUTO_JOIN_WORKSPACE_ID` | Auto-join workspace ID for new users | - | `workspace-id-123` |

## Cache Configuration

| Variable | Description | Default | Example |
| --- | --- | --- | --- |
| `CACHE_MEMORY_ONLY` | Use memory-only caching | `false` | `true` |
| `REDIS_URL` | Redis connection URL | - | `redis://localhost:6379` |

## Authentication

| Variable | Description | Default | Example |
| --- | --- | --- | --- |
| `DISABLE_ACCOUNT` | Disable account-based authentication | `false` | `true` |
| `AUTH_SECRET` | Authentication secret | MD5 of JWT secret | `your-auth-secret` |
| `AUTH_RESTRICT_EMAIL` | Restrict registration to specific email domains | - | `@example.com` |
| `AUTH_USE_SECURE_COOKIES` | Use secure cookies for authentication | `false` | `true` |

### Email Authentication and Email Invitation

| Variable | Description | Default | Example |
| --- | --- | --- | --- |
| `EMAIL_SERVER` | SMTP server for email | - | `smtp://user:pass@smtp.example.com:587` |
| `EMAIL_FROM` | Email sender address | - | `noreply@example.com` |

### GitHub Authentication

| Variable | Description | Default | Example |
| --- | --- | --- | --- |
| `AUTH_GITHUB_ID` | GitHub OAuth client ID | - | `your-github-client-id` |
| `AUTH_GITHUB_SECRET` | GitHub OAuth client secret | - | `your-github-client-secret` |

### Google Authentication

| Variable | Description | Default | Example |
| --- | --- | --- | --- |
| `AUTH_GOOGLE_ID` | Google OAuth client ID | - | `your-google-client-id` |
| `AUTH_GOOGLE_SECRET` | Google OAuth client secret | - | `your-google-client-secret` |

### Custom OAuth/OIDC Authentication

| Variable | Description | Default | Example |
| --- | --- | --- | --- |
| `AUTH_CUSTOM_ID` | Custom OAuth/OIDC client ID | - | `your-custom-client-id` |
| `AUTH_CUSTOM_SECRET` | Custom OAuth/OIDC client secret | - | `your-custom-client-secret` |
| `AUTH_CUSTOM_NAME` | Custom provider name | `Custom` | `Enterprise SSO` |
| `AUTH_CUSTOM_TYPE` | Authentication type | `oidc` | `oauth` |
| `AUTH_CUSTOM_ISSUER` | OIDC issuer URL | - | `https://auth.example.com` |

## AI Features

| Variable | Description | Default | Example |
| --- | --- | --- | --- |
| `SHARED_OPENAI_API_KEY` | OpenAI API key | - | `your-openai-api-key` |
| `SHARED_OPENAI_BASE_URL` | Custom OpenAI API URL | - | `https://api.openai.com/v1` |
| `SHARED_OPENAI_MODEL_NAME` | OpenAI model to use | `gpt-4o` | `gpt-3.5-turbo` |
| `SHARED_OPENAI_TOKEN_CALC_CONCURRENCY` | Token calculation concurrency | `5` | `10` |
| `DEBUG_AI_FEATURE` | Debug AI features | `false` | `true` |

## ClickHouse Configuration

| Variable | Description | Default | Example |
| --- | --- | --- | --- |
| `CLICKHOUSE_URL` | ClickHouse database URL | - | `http://localhost:8123` |
| `CLICKHOUSE_USER` | ClickHouse username | - | `default` |
| `CLICKHOUSE_PASSWORD` | ClickHouse password | - | `your-password` |
| `CLICKHOUSE_DATABASE` | ClickHouse database name | - | `tianji` |
| `CLICKHOUSE_DEBUG` | Enable ClickHouse debugging | `false` | `true` |
| `CLICKHOUSE_DISABLE_SYNC` | Disable ClickHouse synchronization | `false` | `true` |
| `CLICKHOUSE_SYNC_BATCH_SIZE` | Synchronization batch size | `10000` | `5000` |
| `CLICKHOUSE_ENABLE_FALLBACK` | Enable ClickHouse fallback | `true` | `false` |
| `CLICKHOUSE_HEALTH_CHECK_INTERVAL` | Health check interval (ms) | `30000` | `60000` |
| `CLICKHOUSE_MAX_CONSECUTIVE_FAILURES` | Maximum consecutive failures | `3` | `5` |
| `CLICKHOUSE_RETRY_INTERVAL` | Retry interval (ms) | `5000` | `10000` |

## Billing System (LemonSqueezy)

| Variable | Description | Default | Example |
| --- | --- | --- | --- |
| `ENABLE_BILLING` | Enable billing functionality | `false` | `true` |
| `LEMON_SQUEEZY_SIGNATURE_SECRET` | LemonSqueezy webhook signature secret | - | `your-signature-secret` |
| `LEMON_SQUEEZY_API_KEY` | LemonSqueezy API key | - | `your-api-key` |
| `LEMON_SQUEEZY_STORE_ID` | LemonSqueezy store ID | - | `your-store-id` |
| `LEMON_SQUEEZY_SUBSCRIPTION_FREE_ID` | Free tier subscription variant ID | - | `free-variant-id` |
| `LEMON_SQUEEZY_SUBSCRIPTION_PRO_ID` | Pro tier subscription variant ID | - | `pro-variant-id` |
| `LEMON_SQUEEZY_SUBSCRIPTION_TEAM_ID` | Team tier subscription variant ID | - | `team-variant-id` |

## Sandbox Configuration

| Variable | Description | Default | Example |
| --- | --- | --- | --- |
| `USE_VM2` | Use VM2 for sandbox execution | `false` | `true` |
| `SANDBOX_MEMORY_LIMIT` | Memory limit for sandbox (MB) | `16` | `32` |
| `PUPPETEER_EXECUTABLE_PATH` | Custom path to Puppeteer executable | - | `/usr/bin/chromium` |

## Maps Integration

| Variable | Description | Default | Example |
| --- | --- | --- | --- |
| `AMAP_TOKEN` | AMap (Gaode) API token | - | `your-amap-token` |
| `MAPBOX_TOKEN` | Mapbox API token | - | `your-mapbox-token` |

## Telemetry

| Variable | Description | Default | Example |
| --- | --- | --- | --- |
| `DISABLE_ANONYMOUS_TELEMETRY` | Disable anonymous telemetry | `false` | `true` |
| `CUSTOM_TRACKER_SCRIPT_NAME` | Custom tracker script name | - | `custom-tracker.js` |

## Setting Environment Variables

You can set these environment variables in different ways:

1. Set them directly in your deployment environment (Docker, Kubernetes, etc.)

2. For Docker deployments, you can use environment variables in your docker-compose.yml:

```yaml
services:
  tianji:
    image: moonrailgun/tianji:latest
    environment:
      - PORT=3000
      - ALLOW_REGISTER=true
```

## Boolean Values

For boolean environment variables, you can use either `"1"` or `"true"` to enable the feature, and either omit the variable or set it to any other value to disable it.


===== FILE: docs/install/kubernetes/helm.md =====

# Install in Helm

Helm is a tool that streamlines installing and managing Kubernetes applications. Although helm you can easy and quickly enjoy tianji in kubernetes.

## Add repo

At first, you should add msgbyte charts registry into helm repo list.

```bash
helm repo add msgbyte https://msgbyte.github.io/charts
```

Now you can search tianji with `helm search` command.

```bash
helm search repo tianji
```

## Install 

Then, feel free to install with one command:

```bash
helm install tianji msgbyte/tianji
```

Its will bring you a pg database and tianji.


===== FILE: docs/install/manual.md =====

# Install without docker

Use docker to install `Tianji` is best way which you dont need consider about enviroment problem.

But if your server not support dockerize, you can try to install by manual.

## Requirements

You need:

- [Node.js](https://nodejs.org/en/download/) 18.12+ / 20.4+
- [pnpm](https://pnpm.io/) 9.x(9.7.1 better)
- [Git](https://git-scm.com/downloads)
- [postgresql](https://www.postgresql.org/)
- [pm2](https://pm2.keymetrics.io/) - For running Tianji in the background
- [apprise](https://github.com/caronc/apprise) - optional, if you need it to notify

## Clone Code and Build

```bash
git clone https://github.com/msgbyte/tianji.git
cd tianji
pnpm install

pnpm build
```

## Prepare Environment File

Create a `.env` file in `src/server`

```ini
DATABASE_URL="postgresql://user:pass@127.0.0.1:5432/tianji?schema=public"
JWT_SECRET="replace-me-with-a-random-string"
```

Make sure your database url is correct. and dont remember create database before.

For more environment can check this document [environment](./environment.md)

> if you can, better to make sure your encoding is en_US.utf8, for example: `createdb -E UTF8 -l en_US.utf8 tianji`

## Run server

```bash
npm install pm2 -g && pm2 install pm2-logrotate

# Init db migrate
cd src/server
pnpm db:migrate:apply

# Start Server
pm2 start ./dist/src/server/main.js --name tianji
```

Default, `Tianji` will run on `http://localhost:12345`

## Update Code to new Version

```bash
# Checkout new release/tags
cd tianji
git fetch --tags
git checkout -q <version>

# Update dependencies
pnpm install

# Build project
pnpm build

# Run db migrations
cd src/server
pnpm db:migrate:apply

# Restart Server
pm2 restart tianji
```

# Frequently Asked Questions

## Install `isolated-vm` failed

If you are using python 3.12, its will report error like this:

```
ModuleNotFoundError: No module named 'distutils'
```

Its because of python 3.12 remove `distutils` from builtin module. now we have good resolution about it.

You can switch your python version from 3.12 to 3.9 can resolve it.

### How to resolve it in brew controlled python

```bash
brew install python@3.9
rm /opt/homebrew/bin/python3
ln -sf /opt/homebrew/bin/python3 /opt/homebrew/bin/python3.9
```

then you can check version with `python3 --version`


===== FILE: docs/install/other/install-in-aapanel.md =====

# Install in aaPanel

## Intro

`aaPanel` is a powerful and easy-to-use web hosting control panel for linux server. one-click install LNMP/LAMP/OpenLiteSpeed developing environment and software.

[Click here to deploy aaPanel in your server](https://www.aapanel.com/new/download.html?r=dk_tianji)

> Applicable only for aaPanel version 7.0.11 and above.

## Update List and Search

Navbar `docker` -> `one click install` -> `search tianji`

![](/img/docs/aapanel/1.png)

> If you can't find it, please click `Update app list` button.

## Fill form to install

![](/img/docs/aapanel/2.png)

you can fill the form quickly to install.

and only just need to wait for a moment. then you can open your own Tianji in your browser.

> Url: `http://<your-ip>:12345`  
> Default username: `admin`  
> Default password: `admin`


===== FILE: docs/install/telemetry.md =====

# Telemetry

## About Anonymous Telemetry  

To enhance the user experience, we have **enabled anonymous telemetry by default** in your self-hosted Tianji service.  

We understand that data privacy is important to you. Rest assured, this telemetry has the following characteristics:  

- **Completely Anonymous**: We do not collect any personally identifiable information or service content.  
- **Extremely Limited Scope**: It only includes basic usage data, helping us minimally understand Tianji's usage to continuously improve the product.  
- **Fully Transparent**: All telemetry data transmissions can be observed in your service’s network panel, ensuring complete transparency.  

Additionally, we fully respect your choice. If you prefer to **disable anonymous telemetry entirely**, simply set the following environment variable:  

```bash
DISABLE_ANONYMOUS_TELEMETRY=1
```

For detailed configuration instructions, please refer to the Environment Variables Documentation.

With this approach, we aim to improve Tianji while ensuring privacy and security. If you have any questions or feedback, please feel free to reach out to us!


===== FILE: docs/install/troubleshooting.md =====

# Troubleshooting

This document collects common issues and their solutions that you may encounter when using Tianji.

## WebSocket Connection Issues

### Problem Description

When using HTTPS services, other functions work normally, but WebSocket service cannot connect properly, which manifests as:

- The connection status indicator in the bottom left corner shows gray
- Server page list shows counts but no actual content

### Root Cause

This issue is usually caused by improper WebSocket forwarding policies in reverse proxy software. In HTTPS environments, WebSocket connections require correct Cookie security policies.

### Solution

You can resolve this issue by setting the following environment variable:

```bash
AUTH_USE_SECURE_COOKIES=true
```

This setting forces the application to treat cookies passed by the browser as encrypted cookies, thereby resolving WebSocket connection issues.

#### Configuration Methods

**Docker Environment:**
```yaml
# docker-compose.yml
services:
  tianji:
    environment:
      - AUTH_USE_SECURE_COOKIES=true
```

**Direct Deployment:**
```bash
export AUTH_USE_SECURE_COOKIES=true
```

### Verification Steps

After configuration, restart the service and check:

1. The bottom left connection status indicator should show green
2. Server pages should display real-time data normally
3. WebSocket connections should be established properly in browser developer tools

---

*If you encounter other issues, feel free to submit an [Issue](https://github.com/msgbyte/tianji/issues) or contribute solutions to this documentation.*


===== FILE: docs/intro.md =====

# Introduction

## What is Tianji

One sentence to summarize:

**Tianji** = **Website Analytics** + **Uptime Monitor** + **Server Status**

### Why is it called Tianji?

Tianji(天机, pronunciation Tiān Jī) in chinese which means **Heavenly Opportunity** or **Strategy**

The characters 天 (Tiān) and 机 (Jī) can be translated as "heaven" and "machine" or "mechanism" respectively. When combined, it might refer to a strategic or opportunistic plan or opportunity that seems to be orchestrated by a higher power or a celestial force.

## Motivation

During our observations of the website. We often need to use multiple applications together. For example, we need analysis tools such as `GA`/`umami` to check pv/uv and the number of visits to each page, we need an uptime monitor to check the network quality and connectivity of the server, and we need to use prometheus to obtain the status reported by the server to check the quality of the server. In addition, if we develop an application that allows open source deployment, we often need a telemetry system to help us collect the simplest information about other people's deployment situations.

I think these tools should serve the same purpose, so is there an application that can integrate these common needs in a lightweight way? After all, most of the time we don't need very professional and in-depth functions. But in order to achieve comprehensive monitoring, I need to install so many services.

It's good to specialize in one thing, if we are experts in related abilities we need such specialized tools. But for most users who only have lightweight needs, an **All-in-One** application will be more convenient and easier to use.

## Installation

Install Tianji with Docker is very simple. just make sure you have been install docker and docker-compose plugin

and then, run those command in anywhere:

```bash
wget https://raw.githubusercontent.com/msgbyte/tianji/master/docker-compose.yml
docker compose up -d
```

> Default account is **admin**/**admin**, please change password ASAP.

## Community

Join our thriving community to connect with fellow users, share experiences, and stay updated on the latest features and developments. Collaborate, ask questions, and contribute to the growth of the Tianji community.

- [GitHub](https://github.com/msgbyte/tianji)
- [Discord](https://discord.gg/8Vv47wAEej)
- [Stack Overflow](https://stackoverflow.com/questions/tagged/tianji)


===== FILE: docs/mcp/mcp.md =====

# Integration with MCP

<a href="https://cursor.com/install-mcp?name=tianji&config=eyJ0eXBlIjoic3RkaW8iLCJjb21tYW5kIjoibnB4IC15IHRpYW5qaS1tY3Atc2VydmVyIiwiZW52Ijp7IlRJQU5KSV9CQVNFX1VSTCI6IiIsIlRJQU5KSV9BUElfS0VZIjoiIiwiVElBTkpJX1dPUktTUEFDRV9JRCI6IiJ9fQ%3D%3D"><em><img src="https://cursor.com/deeplink/mcp-install-light.svg" alt="Add tianji MCP server to Cursor" height="32" /></em></a>
<br />
[![Add to Kiro](https://kiro.dev/images/add-to-kiro.svg)](https://kiro.dev/launch/mcp/add?name=tianji&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22tianji-mcp-server%22%5D%2C%22env%22%3A%7B%22TIANJI_BASE_URL%22%3A%22https%3A%2F%2Ftianji.example.com%22%2C%22TIANJI_API_KEY%22%3A%22%3Cyour-api-key%3E%22%2C%22TIANJI_WORKSPACE_ID%22%3A%22%3Cyour-workspace-id%3E%22%7D%7D)

## Introduction

Tianji MCP Server is a server based on Model Context Protocol (MCP) that serves as a bridge between AI assistants and the Tianji platform. It exposes Tianji platform's survey functionality to AI assistants through the MCP protocol. This server provides the following core features:

- Query survey results
- Get detailed survey information
- Get all surveys in a workspace
- Get website list

## Installation Methods

### NPX Installation

You can use Tianji MCP Server by adding the following configuration to your AI assistant's configuration file:

```json
{
  "mcpServers": {
    "tianji": {
      "type": "stdio",
      "command": "npx",
      "args": [
        "-y",
        "tianji-mcp-server"
      ],
      "env": {
        "TIANJI_BASE_URL": "https://tianji.example.com",
        "TIANJI_API_KEY": "<your-api-key>",
        "TIANJI_WORKSPACE_ID": "<your-workspace-id>"
      }
    }
  }
}
```

### Environment Variable Configuration

Before using Tianji MCP Server, you need to set the following environment variables:

```bash
# Tianji platform API base URL
TIANJI_BASE_URL=https://tianji.example.com

# Tianji platform API key
TIANJI_API_KEY=your_api_key_here

# Tianji platform workspace ID
TIANJI_WORKSPACE_ID=your_workspace_id_here
```

### Getting an API Key

You can obtain a Tianji platform API key by following these steps:

1. After logging into the Tianji platform, click on your **profile picture** in the top right corner
2. Select **Profile** from the dropdown menu
3. On the profile page, find the **API Keys** option
4. Click on create new key, and follow the prompts to complete the key creation

## Usage Instructions

Tianji MCP Server provides a series of tools that can interact with AI assistants through the MCP protocol. Below are detailed descriptions of each tool:

### Query Survey Results

Use the `tianji_get_survey_results` tool to query result data for a specific survey.

**Parameters:**

- `workspaceId`: Tianji workspace ID (defaults to the value configured in environment variables)
- `surveyId`: Survey ID
- `limit`: Limit on the number of records returned (default 20)
- `cursor`: Pagination cursor (optional)
- `startAt`: Start time, ISO format, example: 2023-10-01T00:00:00Z
- `endAt`: End time, ISO format, example: 2023-10-31T23:59:59Z
- `filter`: Filter conditions (optional)

### Get Survey Information

Use the `tianji_get_survey_info` tool to get basic information about a specific survey.

**Parameters:**

- `workspaceId`: Tianji workspace ID (defaults to the value configured in environment variables)
- `surveyId`: Survey ID

### Get All Survey List

Use the `tianji_get_all_survey_list` tool to get a list of all surveys in the workspace.

**Parameters:**

- `workspaceId`: Tianji workspace ID (defaults to the value configured in environment variables)

### Get Website List

Use the `tianji_get_website_list` tool to get a list of websites in the workspace.

**Parameters:**

- `workspaceId`: Tianji workspace ID (defaults to the value configured in environment variables)

## Usage Examples

Below are examples of natural language interactions between users and AI assistants integrated with Tianji MCP Server:

### Example 1: Get All Survey List

```
User: Help me view all my survey questionnaire lists

AI Assistant: I'll get all your survey questionnaire lists for you.
[AI Assistant calls tianji_get_all_survey_list through MCP protocol to get data]
Based on the data in your workspace, you currently have the following surveys:
1. User Experience Satisfaction Survey (created on 2023-09-15)
2. Product Feature Feedback Survey (created on 2023-10-01)
3. Website Usability Assessment (created on 2023-10-20)
```

### Example 2: Get Specific Survey Details

```
User: Please tell me the details of the "User Experience Satisfaction Survey"

AI Assistant: I'll query the details of that survey for you.
[AI Assistant calls tianji_get_survey_info through MCP protocol to get data]
Details of the "User Experience Satisfaction Survey":
- Creation time: 2023-09-15
- Status: Active
- Number of questions: 10
- Responses collected: 128
- Most recent response: 2023-10-25
```

### Example 3: Query Survey Results

```
User: Help me analyze the October results of the "Product Feature Feedback Survey"

AI Assistant: I'll analyze the October results of the "Product Feature Feedback Survey".
[AI Assistant calls tianji_get_survey_results through MCP protocol to get data for the specific time range]
Analysis of the October "Product Feature Feedback Survey" results:
- Total responses: 42
- Most popular feature: Automated Reports (85% positive feedback)
- Feature most in need of improvement: Mobile adaptation (mentioned by 62%)
- Average satisfaction rating: 4.2/5
- Main user suggestions: Add data export functionality, optimize page loading speed
```


===== FILE: docs/monitor/custom-script.md =====

# Custom Script

Compared with traditional monitoring services, **Tianji** supports custom scripts to support more customized scenarios.

Essentially, you can understand it as a restricted, memory safe JavaScript runtime that accepts a number to display on your chart. The most common scenario is the time required for network requests to access an url. Of course, it can also be other things, such as your OpenAI balance, your github star number, and all the information that can be expressed in numbers.

if this script return -1, its means this work is failed, and try to send notification to you, just like normal monitor.

If you want to view the trend of a number's changes, opening the trending mode can help you better discover subtle changes in the number

Here is some example:

## Examples

### get tailchat available service number from health endpoint

```js
const res = await request({
  url: 'https://<tailchat-server-api>/health'
})

if(!res || !res.data || !res.data.services) {
  return -1
}

return res.data.services.length
```

### get github star count

```js
const res = await request({
  url: 'https://api.github.com/repos/msgbyte/tianji'
})

return res.data.stargazers_count ?? -1
```

replace `msgbyte/tianji` to your own repo name

### get docker pull count

```js
const res = await request({
  url: "https://hub.docker.com/v2/repositories/moonrailgun/tianji/"
});

return res.data.pull_count;
```

replace `moonrailgun/tianji` to your own image name

### example for match text

```js
const start = Date.now();
const res = await request({
  url: "https://example.com/"
});

const usage = Date.now() - start;

const matched = /maintain/.test(String(res.data));

if(matched) {
  return -1;
}

return usage;
```

return `-1` means somthing wrong. in this case, its means in html body include `maintain` text.

### or more

Very very welcome to submit your script in this page. Tianji is driven by open source community.


===== FILE: docs/monitor/push-monitor.md =====

# Push Monitor

Push Monitor is a monitoring method where your application actively sends heartbeat signals to **Tianji** instead of Tianji checking your service. This is particularly useful for monitoring background tasks, cron jobs, or services behind firewalls that cannot be accessed directly.

## How It Works

1. **Tianji** provides you with a unique push endpoint URL
2. Your application sends HTTP POST requests to this endpoint at regular intervals
3. If no heartbeat is received within the configured timeout period, Tianji triggers an alert

## Configuration

When creating a Push Monitor, you need to configure:

- **Monitor Name**: A descriptive name for your monitor
- **Timeout**: The maximum time (in seconds) to wait between heartbeats before considering the service down
- **Recommended Interval**: How often your application should send heartbeats (typically the same as timeout)

## Push Endpoint Format

```
POST https://tianji.example.com/api/push/{pushToken}
```

### Status Parameters

- **Normal Status**: Send without parameters or with `?status=up`
- **Down Status**: Send with `?status=down` to manually trigger an alert
- **Custom Message**: Add `&msg=your-message` to include additional information
- **Custom Value**: Add `&value=123` to track numeric values

## Examples

### Basic Heartbeat (cURL)

```bash
# Send heartbeat every 60 seconds
while true; do
  curl -X POST "https://tianji.example.com/api/push/<your-push-token>"
  sleep 60
done
```

### JavaScript/Node.js

```javascript
// Send heartbeat every 60 seconds
setInterval(async () => {
  try {
    await fetch('https://tianji.example.com/api/push/<your-push-token>', { 
      method: 'POST' 
    });
    console.log('Heartbeat sent successfully');
  } catch (error) {
    console.error('Failed to send heartbeat:', error);
  }
}, 60000);
```

### Python

```python
import requests
import time

def send_heartbeat():
    try:
        response = requests.post('https://tianji.example.com/api/push/<your-push-token>')
        print('Heartbeat sent successfully')
    except Exception as e:
        print(f'Failed to send heartbeat: {e}')

# Send heartbeat every 60 seconds
while True:
    send_heartbeat()
    time.sleep(60)
```

## Use Cases

### 1. Cron Jobs

Monitor the execution of scheduled tasks:

```bash
#!/bin/bash
# your-cron-job.sh

# Your actual job logic here
./run-backup.sh

# Send success signal
if [ $? -eq 0 ]; then
  curl -X POST "https://tianji.example.com/api/push/<your-push-token>?status=up&msg=backup-completed"
else
  curl -X POST "https://tianji.example.com/api/push/<your-push-token>?status=down&msg=backup-failed"
fi
```

### 2. Background Services

Monitor long-running background processes:

```python
import requests
import time
import threading

class ServiceMonitor:
    def __init__(self, push_url):
        self.push_url = push_url
        self.running = True
        
    def start_heartbeat(self):
        def heartbeat_loop():
            while self.running:
                try:
                    requests.post(self.push_url)
                    time.sleep(30)  # Send every 30 seconds
                except Exception as e:
                    print(f"Heartbeat failed: {e}")
        
        thread = threading.Thread(target=heartbeat_loop)
        thread.daemon = True
        thread.start()

# Usage
monitor = ServiceMonitor('https://tianji.example.com/api/push/<your-push-token>')
monitor.start_heartbeat()

# Your main service logic here
while True:
    # Do your work
    time.sleep(1)
```

### 3. Database Sync Jobs

Monitor data synchronization tasks:

```python
import requests
import schedule
import time

def sync_data():
    try:
        # Your data sync logic here
        result = perform_data_sync()
        
        if result.success:
            requests.post(
                'https://tianji.example.com/api/push/<your-push-token>',
                params={'status': 'up', 'msg': f'synced-{result.records}-records'}
            )
        else:
            requests.post(
                'https://tianji.example.com/api/push/<your-push-token>',
                params={'status': 'down', 'msg': 'sync-failed'}
            )
    except Exception as e:
        requests.post(
            'https://tianji.example.com/api/push/<your-push-token>',
            params={'status': 'down', 'msg': f'error-{str(e)}'}
        )

# Schedule to run every hour
schedule.every().hour.do(sync_data)

while True:
    schedule.run_pending()
    time.sleep(1)
```

## Best Practices

1. **Set Appropriate Timeouts**: Configure timeout values based on your application's needs. For frequent tasks, use shorter timeouts. For periodic jobs, use longer timeouts.

2. **Handle Network Failures**: Implement retry logic in your heartbeat code to handle temporary network issues.

3. **Use Meaningful Messages**: Include descriptive messages with your heartbeats to provide context when reviewing logs.

4. **Monitor Critical Paths**: Place heartbeat calls at critical points in your application flow, not just at the beginning.

5. **Exception Handling**: Send a "down" status when an exception occurs in your application.

## Troubleshooting

### Common Issues

**No heartbeats received**:
- Verify the push token is correct
- Check network connectivity from your application to Tianji
- Ensure your application is actually running the heartbeat code

**Frequent false alarms**:
- Increase the timeout value
- Check if your application is experiencing performance issues
- Review network stability between your app and Tianji

**Missing heartbeats**:
- Add error handling and logging to your heartbeat code
- Consider implementing retry logic for failed requests
- Monitor your application's resource usage

## Security Considerations

- Keep your push tokens secure and don't expose them in public repositories
- Use HTTPS endpoints to encrypt data in transit
- Consider rotating push tokens periodically
- Limit the frequency of heartbeats to avoid overwhelming your Tianji instance

Push monitoring provides a reliable way to monitor services that traditional ping-based monitoring cannot reach, making it an essential tool for comprehensive infrastructure monitoring.


===== FILE: docs/notification/webhook.md =====

# Webhook

If you need a more flexible notification method, you can try using custom webhooks to notify your messages. This way, you can integrate Tianji's notifications into any system.

### Example Result

Tianji system will send a POST request with below example content.

```json
{
    "notification": {
        "workspaceId": "xxxxxxxxxxx",
        "name": "New Notification",
        "type": "webhook",
        "payload": {
            "webhookUrl": "example.com"
        }
    },
    "title": "New Notification Notification Testing",
    "content": "Tianji: Insight into everything\\nThis is Notification Testing from New Notification\\n[image]",
    "raw": [
        {
            "type": "title",
            "level": 2,
            "content": "Tianji: Insight into everything"
        },
        {
            "type": "text",
            "content": "This is Notification Testing from New Notification"
        },
        {
            "type": "newline"
        },
        {
            "type": "image",
            "url": "https://tianji.dev/img/social-card.png"
        }
    ],
    "time": "2024-06-19T15:41:09.390Z"
}
```


===== FILE: docs/server-status/kubernetes/reporter-daemonset.md =====

# Deploy Reporter as DaemonSet

If you are running Tianji inside Kubernetes, you may want to collect each node's
system metrics. The easiest way is to run `tianji-reporter` as a DaemonSet so it
runs on every node.

1. Edit `docker/k8s/reporter-daemonset.yaml` and replace the `TIANJI_SERVER_URL` and `TIANJI_WORKSPACE_ID` values with your actual server address and workspace ID.
2. Apply the manifest:

```bash
kubectl apply -f docker/k8s/reporter-daemonset.yaml
```

Every node will start a `tianji-reporter` container which reports system
statistics to your Tianji instance. You can check the logs of a specific pod to
ensure it works:

```bash
kubectl logs -l app=tianji-reporter -f
```

Once the pods are running, the **Servers** page in Tianji will list your
Kubernetes nodes just like regular machines.


===== FILE: docs/server-status/server-status-page.md =====

# Server Status Page

You can create a server status page for user to show your server status to public which wanna make others know.

## Configure custom domain

You can config your status page in your own domain, for example: `status.example.com`

set it in page config, and create a `CNAME` record in your DNS dashboard.

```
CNAME status.example.com tianji.example.com
```

then you can visit custom `status.example.com` to your page.

### Troubleshooting

If you will throw 500 error, it looks like your Reverse Proxy is not configured correctly.

Please make sure your reverse proxy include your new status route.

for example:
```
server {
  listen 80;
  server_name tianji.example.com status.example.com;
  listen 443 ssl;
}
```


===== FILE: docs/server-status/server-status-reporter.md =====

# Server Status Reporter

you can report your server status easily with tianji reporter

you can download from [https://github.com/msgbyte/tianji/releases](https://github.com/msgbyte/tianji/releases)

## Usage

```
Usage of tianji-reporter:
  --interval int
        Input the INTERVAL, seconed (default 5)
  --mode http
        The send mode of report data, you can select: `http` or `udp`, default is `http` (default "http")
  --name string
        The identification name for this machine
  --url string
        The http url of tianji, for example: https://tianji.dev
  --vnstat
        Use vnstat for traffic statistics, linux only
  --workspace string
        The workspace id for tianji, this should be a uuid
```

The **url** and **workspace** is required, its means you will report your service to which host and which workspace.

Default a server node name will be same with hostname, so you can custom your name with `--name` which can help you identify server.

## Auto install script

You can get your auto install script in `Tianji` -> `Servers` -> `Add` -> `Auto` tab

its will auto download reporter and create linux service in your machine. so its need root permission.

### Uninstall

if you wanna uninstall reporter service, you can use this command like:
```bash
curl -o- https://tianji.example.com/serverStatus/xxxxxxxxxxxxxxxxxxx/install.sh?url=https://tianji.example.com | sudo bash -s uninstall
``` 

The main change is to append `-s uninstall` to your install command.

## Kubernetes

If your servers are running in a Kubernetes cluster, you can deploy the reporter as a DaemonSet so each node reports metrics automatically. See [Deploy Reporter as DaemonSet](./kubernetes/reporter-daemonset.md) for details.

## Q&A

### How to check tianji reporter service log?

If you install with auto install script, tianji will help you install a service which named `tianji-reporter` in your linux machine.

You can use this command to check tianji reporter log:

```bash
journalctl -fu tianji-reporter.service
```

### Not found your machine in server tab even report show success

Maybe your tianji is behind a reverse proxy for example `nginx`.

Please make sure your reverse proxy add websocket support

## Why my machine is always offline?

Please check your server datetime.


===== FILE: docs/skill/installation.md =====

# Installation

The skill is just three files. Any modern AI agent (Cursor, Claude Code, Codex, Copilot CLI...) already knows where its own skills directory is — so installation can be as simple as pasting one prompt.

## One-Click Installation (via AI Agent)

Paste the prompt below into your AI agent. It will download the files into the correct skills directory for its platform, then ask you for any missing configuration.

```
Please install the Tianji Data Query Skill into your skills directory:

https://github.com/msgbyte/tianji/tree/master/skills/tianji-data-query

After downloading, check whether these environment variables are set:
  - TIANJI_BASE_URL
  - TIANJI_API_KEY
  - TIANJI_WORKSPACE_ID

If any are missing, ask me for the values.
```

That's it. The agent picks its own skills directory, fetches the files, and prompts you for credentials when needed.

## Manual Installation

If you'd rather install it by hand, pick the target directory for your agent and run:

```bash
DEST="$HOME/.cursor/skills/tianji-data-query"   # or whatever your agent uses
mkdir -p "$DEST/references"

BASE="https://raw.githubusercontent.com/msgbyte/tianji/master/skills/tianji-data-query"
curl -fSL "$BASE/SKILL.md"                          -o "$DEST/SKILL.md"
curl -fSL "$BASE/references/api-endpoints.md"       -o "$DEST/references/api-endpoints.md"
curl -fSL "$BASE/references/openapi-readonly.json"  -o "$DEST/references/openapi-readonly.json"
```

### Skills directory by agent

| Agent | Directory |
|-------|-----------|
| Cursor (personal) | `~/.cursor/skills/tianji-data-query/` |
| Cursor (project)  | `<project-root>/.cursor/skills/tianji-data-query/` |
| Claude Code       | `~/.claude/skills/tianji-data-query/` |
| Codex             | `~/.codex/skills/tianji-data-query/` |
| Codex (alt)       | `~/.agents/skills/tianji-data-query/` |

## Required Environment Variables

The skill expects three values. Export them in your shell rc, or set them in your agent's skill config:

```bash
# Tianji instance base URL
TIANJI_BASE_URL=https://tianji.example.com

# API key for authentication
TIANJI_API_KEY=your_api_key_here

# Default workspace ID
TIANJI_WORKSPACE_ID=your_workspace_id_here
```

### Getting an API Key

1. Log in to your Tianji instance and click your **profile picture** in the top right corner.
2. Select **Profile** from the dropdown menu.
3. Find the **API Keys** section.
4. Click **Create new key** and follow the prompts.

## Next Steps

After installation, head back to [Integration with Agent Skill](./skill.md) to see usage examples, the comparison with the MCP Server, and how the skill handles sensitive data.


===== FILE: docs/skill/skill.md =====

# Integration with Agent Skill

## Introduction

The **Tianji Data Query Skill** is a lightweight, agent-agnostic skill bundle that lets AI agents (Cursor, Claude Code, Codex, Copilot CLI, etc.) query the Tianji platform directly through its read-only OpenAPI.

It follows the [agentskills.io](https://agentskills.io/specification) specification — a single `SKILL.md` plus reference files. No long-running process, no extra runtime.

:::tip Get started
See the [Installation Guide](./installation.md) for one-click and manual setup.
:::

**What it covers:** 69 GET endpoints across 14 service domains:

- **Website** — traffic stats, pageviews, geo distribution, Lighthouse reports
- **Monitor** — uptime status, recent check data, monitor events
- **Survey** — survey responses, result stats, AI categories
- **Telemetry** — custom event counts, telemetry pageviews, metrics
- **Feed** — channels, event streams, feed states
- **Application** — app store reviews, app info, event stats
- **Billing / AI Gateway / Worker / Page / Workspace / Global / AuditLog**

## Skill vs MCP Server

Tianji offers two ways to integrate with AI agents. Pick the one that fits your workflow:

| | Agent Skill | [MCP Server](/docs/mcp) |
|--|--|--|
| **Form** | Plain documentation files (`SKILL.md` + references) | A long-running Node.js process |
| **Runtime** | None — agent uses `curl` or built-in HTTP tools | `npx tianji-mcp-server` |
| **Coverage** | 69 GET endpoints (read-only, full surface) | A curated subset of tools (read + some write) |
| **Setup** | Drop files into the agent's skills directory | Add MCP config to the agent's config file |
| **Best for** | Cursor / Claude Code / Codex / any agent following the agentskills.io spec | Agents with first-class MCP support |

You can use both at the same time without conflict.

## Usage Examples

Once installed, you can ask your AI agent natural-language questions and it will pick the right endpoint:

### Example 1: Website traffic overview

```
User: Show me the pageviews of my main website over the past 7 days.

Agent: [Calls GET /open/workspace/{workspaceId}/website/all to find the site]
       [Then GET /open/workspace/{workspaceId}/website/{websiteId}/pageviews
        with startAt/endAt covering 7 days]
       Your main website received 12,438 pageviews over the past 7 days,
       with a peak of 2,103 on Tuesday. Top referrer: google.com (43%).
```

### Example 2: Monitor health check

```
User: Are any of my monitors down right now?

Agent: [Calls GET /open/workspace/{workspaceId}/monitor/all]
       [Then GET /open/workspace/{workspaceId}/monitor/{monitorId}/status
        for each monitor]
       2 monitors are currently DOWN:
       - api.example.com (HTTP 503, last UP 12 min ago)
       - db-replica (TCP timeout, last UP 1h ago)
       The other 8 monitors are healthy.
```

### Example 3: Survey result analysis

```
User: Summarise the responses to the "Q4 Customer Feedback" survey.

Agent: [Calls GET /open/workspace/{workspaceId}/survey/all to resolve the ID]
       [Then GET /open/workspace/{workspaceId}/survey/{surveyId}/stats]
       [Then GET /open/workspace/{workspaceId}/survey/{surveyId}/result/list
        with a pagination cursor]
       "Q4 Customer Feedback" received 218 responses. Average satisfaction
       4.3/5. Top theme: dashboard performance (mentioned 47 times). Most
       requested feature: dark mode (31 mentions).
```

## Sensitive Data Handling

Some endpoints may return platform-stored secrets (e.g. `modelApiKey`, `customModelBaseUrl` in AI Gateway responses) or PII (workspace members, audit logs, billing).

The skill instructs agents to:

- **Never display** `apiKey`, `modelApiKey`, `secret`, `token`, `password`, or `credential` fields.
- **Redact or omit** these fields when summarising responses.
- For workspace members / audit logs, only surface non-sensitive metadata (names, roles, timestamps) unless the user explicitly requests full detail.

The bundled `openapi-readonly.json` also pre-redacts these fields at the schema level, so agents cannot accidentally rely on their structure.

## Source

The skill source lives in the Tianji repository under [`skills/tianji-data-query/`](https://github.com/msgbyte/tianji/tree/master/skills/tianji-data-query). Pull requests welcome.


===== FILE: docs/special-thanks.md =====

# Special thanks

## Open source project

Tianji is very inspired by those project, thanks for your contributions to the open source community.

I also loves those project. Salute to these excellent projects!

- [umami](https://github.com/umami-software/umami)
- [uptime kuma](https://github.com/louislam/uptime-kuma)


===== FILE: docs/telemetry/intro.md =====

# Introduce

## Background

As content creators, we often publish our articles on various third-party platforms. However, for those of us who are serious about our content, publishing is just the beginning. We need to continuously monitor our articles' readership over time. Unfortunately, our data collection capabilities are limited to what each platform offers, which heavily depends on the platform's own capabilities. Moreover, when we distribute the same content across different platforms, the readership and visitation data are completely isolated.

As a developer, I create many software applications. However, once I release these applications, I often lose control over them. For example, after releasing a command-line program, I have no way of knowing how users are interacting with it or even how many users are using my application. Similarly, when developing an open-source application, in the past, I could only gauge interest through GitHub stars, leaving me in the dark about actual usage.

Therefore, we need a simple solution that collects minimal information, respecting personal privacy and other restrictions. This solution is telemetry.

## Telemetry

In the field of computing, telemetry is a common technology that involves the minimal and anonymous reporting of information to accommodate privacy concerns while still meeting the basic analytical needs of content creators.

For example, React's Next.js framework collects information using telemetry: [API Reference: Next.js CLI | Next.js (nextjs.org)](https://nextjs.org/docs/app/api-reference/next-cli#telemetry)

Alternatively, by embedding a 1px-sized blank transparent pixel image in an article, it's possible to collect visitor data on websites over which we have no control. Modern browsers and most websites block the insertion of custom scripts due to potential security risks. However, an image appears much more harmless by comparison. Almost all websites allow the loading of third-party images, making telemetry feasible.

## What Information Can We Collect Through an Image?

Surprisingly, receiving a single image request allows us to collect more information than one might expect.

By analyzing network requests, we can obtain the user's IP address, visit time, referrer, and device type. This enables us to analyze traffic patterns, such as peak readership times and trends, demographic distribution, and traffic granularity across different platforms. This information is particularly valuable for marketing and promotional activities.

![](/img/telemetry/1.png)

## How Can We Implement Telemetry?

Telemetry is a straightforward technology that essentially requires an endpoint to receive internet requests. Due to its simplicity, there are few dedicated tools for this purpose. Many may not consider analytics important, or they might be deterred by the initial barriers. However, the demand for such functionality is clear.

Developing a telemetry solution is simple. You just need to create a project, set up a route, collect information from the request body, and return a blank image.

Here's an example using Node.js:

```jsx
router.get(
  '/telemetry.gif',
  async (req, res) => {
    const ip = req.ip;
    const referer = req.header['referer'];
    const userAgent = req.headers['user-agent'];
    
    // Store it in your database
    
    const blankGifBuffer = Buffer.from(
      'R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7',
      'base64'
    );
    res.header('Content-Type', 'image/gif').send(blankGifBuffer);
  }
);
```

If you prefer not to develop your own solution, I recommend Tianji. As an open-source project offering **Website Analytics**, **Uptime Monitoring**, and **Server Status**, Tianji has recently introduced a telemetry feature to assist content creators in reporting telemetry, thereby facilitating better data collection. Most importantly, being open-source means you have control over your data and can aggregate traffic from multiple platforms in one place, avoiding the fragmentation of viewing the same information in different locations.

![](/img/telemetry/2.png)

GitHub: [https://github.com/msgbyte/tianji](https://github.com/msgbyte/tianji) 

Official Website: [https://tianji.dev/](https://tianji.dev/)


===== FILE: docs/telemetry/params.md =====

# Parameters

Here is some example for how to use and config with telemetry image.

All is optional. its will improve your usage in different place.

| name | description |
| -------- | --------- |
| url | default will get referrer url which auto generate by browser, but in some website will dont allow carry this header, so you have to bring it by your self. if Tianji cannot get url in any where, system will ignore and not record this visit |
| name | define telemetry event name, its can be use for distinguish different event but in same telemetry record. |
| title | **[Badge ONLY]**, define badge title |
| start | **[Badge ONLY]**, define badge start count number |
| fullNum | **[Badge ONLY]**, define badge will show full number, default is abbreviated digits(for example: `12345` and `12.3k`) |

## How to use

Its easy to carry params on url

for example:

```
https://tianji.example.com/telemetry/<workspaceId>/<telemetryId>/badge.svg?name=myEvent&url=https://google.com&title=My+Counter&start=100000&fullNum=true
```

If you dont familiar with this, you can check wiki page about this: [https://en.wikipedia.org/wiki/Query_string](https://en.wikipedia.org/wiki/Query_string)


===== FILE: docs/website/framework/docusaurus.md =====

# Use in Docusaurus

In `docusaurus.config.js`:

```js
/** @type {import('@docusaurus/types').Config} */
const config = {
  // ...

  scripts: [
    {
      src: 'https://<your domain>/tracker.js',
      async: true,
      defer: true,
      'data-website-id': '<your-website-id>',
    },
  ],
};

module.exports = config;
```


===== FILE: docs/website/framework/install-in-traefik-with-plugin.md =====

# Install in Traefik with plugin

Tianji provides a Traefik plugin that allows you to easily integrate Tianji website analytics functionality into your Traefik proxy.

## Plugin Overview

[traefik-tianji-plugin](https://github.com/msgbyte/traefik-tianji-plugin) is a Traefik middleware plugin specifically developed for Tianji that can automatically inject the Tianji tracking script into your website without modifying your website code to start collecting visitor data.

## Installing the Plugin

### 1. Add Plugin in Static Configuration

First, you need to add the plugin reference in Traefik's static configuration. The plugin version number references the git tag.

#### YAML Configuration

Add the following to your `traefik.yml` or static configuration file:

```yaml
experimental:
  plugins:
    traefik-tianji-plugin:
      moduleName: "github.com/msgbyte/traefik-tianji-plugin"
      version: "v0.2.1"
```

#### TOML Configuration

```toml
[experimental.plugins.traefik-tianji-plugin]
  moduleName = "github.com/msgbyte/traefik-tianji-plugin"
  version = "v0.2.1"
```

#### Command Line

```bash
--experimental.plugins.traefik-tianji-plugin.modulename=github.com/msgbyte/traefik-tianji-plugin
--experimental.plugins.traefik-tianji-plugin.version=v0.2.1
```

### 2. Configure Middleware

After installing the plugin, you need to configure the middleware in the dynamic configuration.

#### YAML Dynamic Configuration

In your `config.yml` or dynamic configuration file:

```yaml
http:
  middlewares:
    my-tianji-middleware:
      plugin:
        traefik-tianji-plugin:
          tianjiHost: "https://tianji.your-domain.com"
          websiteId: "your-website-id"
```

#### TOML Dynamic Configuration

```toml
[http.middlewares.my-tianji-middleware.plugin.traefik-tianji-plugin]
  tianjiHost = "https://tianji.your-domain.com"
  websiteId = "your-website-id"
```

#### Docker Compose Labels

```yaml
version: '3.7'
services:
  my-app:
    image: nginx:latest
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.my-app.rule=Host(`my-app.local`)"
      - "traefik.http.routers.my-app.middlewares=my-tianji-middleware"
      - "traefik.http.middlewares.my-tianji-middleware.plugin.traefik-tianji-plugin.tianjiHost=https://tianji.your-domain.com"
      - "traefik.http.middlewares.my-tianji-middleware.plugin.traefik-tianji-plugin.websiteId=your-website-id"
```

## Configuration Parameters

### Required Parameters

- **tianjiHost**: The complete URL of your Tianji server
  - Example: `https://tianji.your-domain.com`
  - If using the official hosted service: `https://app.tianji.dev`

- **websiteId**: The website ID created in Tianji
  - Can be found in the website settings of your Tianji admin panel

### Optional Parameters

The plugin also supports other configuration parameters to customize behavior. For specific parameters, please refer to the [GitHub repository documentation](https://github.com/msgbyte/traefik-tianji-plugin).

## Using the Middleware

After configuration, you need to use this middleware in your router:

### YAML Configuration

```yaml
http:
  routers:
    my-app:
      rule: "Host(`my-app.local`)"
      middlewares:
        - "my-tianji-middleware"
      service: "my-app-service"
```

### Docker Compose Labels

```yaml
labels:
  - "traefik.http.routers.my-app.middlewares=my-tianji-middleware"
```

## How It Works

1. When requests pass through the Traefik proxy, the plugin checks the response content
2. If the response is HTML content, the plugin automatically injects the Tianji tracking script
3. The script starts collecting visitor data and sends it to the Tianji server when the page loads

## Important Notes

- Ensure that the Tianji server address is accessible from client browsers
- The website ID must be valid, otherwise data cannot be collected properly
- The plugin only takes effect when the response content type is HTML
- It's recommended to use the latest version of the plugin for optimal performance and features

## Reference

- [Plugin Source Code](https://github.com/msgbyte/traefik-tianji-plugin)
- [Traefik Plugin Documentation](https://doc.traefik.io/traefik/plugins/)


===== FILE: docs/website/track-script.md =====

# Tracker Script

## Installation

To track website events, you just need inject a simple script(< 2 KB) into your website.

the script look like below:

```html
<script async defer src="https://<your-self-hosted-domain>/tracker.js" data-website-id="xxxxxxxxxxxxx"></script>
```

you can get this script code from your **Tianji** website list

## Script Attributes

The tracker script supports the following `data-*` attributes on the `<script>` tag:

| Attribute | Required | Default | Description |
|---|---|---|---|
| `data-website-id` | **Yes** | — | The unique website ID for associating tracking data. The tracker will not initialize without this. |
| `data-host-url` | No | Script `src` origin | The backend server URL. If omitted, it is automatically derived from the script's `src` path. |
| `data-auto-track` | No | `true` | Automatically track page views and route changes. Set to `"false"` to disable. |
| `data-do-not-track` | No | — | When set, respects the browser's Do Not Track (DNT) setting and disables tracking if DNT is enabled. |
| `data-domains` | No | — | A comma-separated list of domains to track (e.g. `"example.com,www.example.com"`). Tracking is only active when the current hostname matches one of these domains. |

### Full Example

```html
<script
  async
  defer
  src="https://example.com/tracker.js"
  data-website-id="clxxxxxxxxxxxxxxxxxx"
  data-host-url="https://analytics.example.com"
  data-auto-track="true"
  data-do-not-track="true"
  data-domains="example.com,www.example.com"
></script>
```

### Disable Tracking via localStorage

You can also disable tracking at runtime by setting a localStorage flag:

```javascript
localStorage.setItem('tianji.disabled', '1');
```

## Report Event

**Tianji** provide a simple way to report user click event, its easy to help you track which action user like and often to use.

This is a very common method in website analysis. You can use it quickly get it by using **Tianji**.

### Basic Usage

After you inject script code into your website, you just need add a `data-tianji-event` in dom attribute.

for example:

```html
<button data-tianji-event="submit-login-form">Login</button>
```

Now, when user click this button, your dashboard will receive new event

### Attach Event Data

You can attach additional data to your events by using `data-tianji-event-{key}` attributes. Any attribute matching this pattern will be collected and sent with the event.

```html
<button 
  data-tianji-event="purchase" 
  data-tianji-event-product="Premium Plan"
  data-tianji-event-price="99"
  data-tianji-event-currency="USD">
  Buy Now
</button>
```

This will send an event named `purchase` with the following data:
```json
{
  "product": "Premium Plan",
  "price": "99",
  "currency": "USD"
}
```

### Track Link Clicks

When using `data-tianji-event` on anchor (`<a>`) tags, **Tianji** handles them specially to ensure the event is tracked before navigation:

```html
<a href="/pricing" data-tianji-event="view-pricing">Check Pricing</a>
```

For internal links (not opening in new tab), the tracker will:
1. Prevent the default navigation
2. Send the tracking event
3. Navigate to the target URL after tracking completes

For external links or links with `target="_blank"`, the event is tracked without blocking the navigation.

### JavaScript API

After the tracker script is loaded, you can also track events programmatically using the `window.tianji` object.

#### Track Custom Events

```javascript
// Simple event tracking
window.tianji.track('button-clicked');

// Event with custom data
window.tianji.track('purchase', {
  product: 'Premium Plan',
  price: 99,
  currency: 'USD'
});

// Track with custom payload object
window.tianji.track({
  website: 'your-website-id',
  name: 'custom-event',
  data: { key: 'value' }
});

// Track using a function (receives current page info)
window.tianji.track((payload) => ({
  ...payload,
  name: 'dynamic-event',
  data: { timestamp: Date.now() }
}));
```

#### Identify Users

You can attach user information to tracking sessions:

```javascript
window.tianji.identify({
  userId: 'user-123',
  email: 'user@example.com',
  plan: 'premium'
});
```

This information will be associated with subsequent events from this user.

## Modify default script name

> This feature available on v1.7.4+

You can use environment `CUSTOM_TRACKER_SCRIPT_NAME` when you start it

for example:
```
CUSTOM_TRACKER_SCRIPT_NAME="my-tracker.js"
```

then you can visit your tracker script with `"https://<your-self-hosted-domain>/my-tracker.js"`

This is to help you avoid some ad-blockers.

You do not need the `.js` suffix. It can be any path you choose, even you can use as `CUSTOM_TRACKER_SCRIPT_NAME="this/is/very/long/path"`

## Tracking Specified Domains Only

Generally the tracker will report all events wherever your site is running. But sometimes we need to ignore events like `localhost`.

Tianji provides an attribute of the tracker script to do that.

You can add `data-domains` into your script. The value should be your root domains to track. Use `,` to separate multiple domains.

```html
<script async defer src="https://<your-self-hosted-domain>/tracker.js" data-website-id="xxxxxxxxxxxxx" data-domains="website.com,www.website.com"></script>
```

Then you can just see the events from these domains.


===== FILE: src/pages/markdown-page.md =====

# Markdown page example

You don't need React to write simple standalone pages.


===== FILE: src/pages/private-policy.md =====

# Tianji Privacy Policy

> Last updated August 26, 2024

This privacy notice for `tianji.msgbye.com` ("we," "us," or "our") explains how and why we collect, store, use, and/or share ("process") your personal information when you use our services ("Services"). This includes actions like:

- Visiting our website at `msgbye.com`, or any other website of ours that links to this privacy notice.
- Interacting with us in other related ways, such as during sales, marketing activities, or events.

If you have any questions or concerns about our privacy practices, reading this privacy notice will help you understand your rights and choices. If you disagree with our policies and practices, please refrain from using our services. For any further questions or concerns, contact us at `moonrailgun@gmail.com`.

## Personal Data We Collect

### Information You Provide

We collect the personal data you voluntarily provide when you register for our Services, express interest in obtaining information about us or our products and Services, participate in activities on the Services, or contact us directly.

The personal data we collect may include, but is not limited to:

- Email addresses
- Usernames
- Passwords

We encrypt sensitive information, such as passwords, to protect your data even if our database is compromised.

## Personal Data We Obtain From Third Parties

### Single Sign-On

If you sign up for our website `tianji.msgbye.com` through Google, you authorize us to collect authentication information, including your username, encrypted access credentials, and any other personal data available through your third-party application account.

## How We Use Your Personal Data

We use your personal data to:

- **Provide, Improve, and Develop the Platform**: We process your data primarily to ensure the proper functioning of our website. We may also use your data to improve our website and offer you the best possible experience. Specifically, we:
  - Operate and maintain your `tianji.msgbye.com` account, allowing you to access and use the Platform. Your email address and password are used to identify you when you log in.
  
- **Maintain a Safe and Secure Environment**: We process your data to enhance the security of our Platform, ensuring a safe experience for all users.

## How We Share Your Personal Data

We do not sell your personal data to external parties.

- **Through Your Profile**: Any personal data you choose to disclose in your public profile on `tianji.msgbye.com` will be visible to other users. They may view your profile information and interact with any public prompts you share.
  
- **Compliance with Laws**: We may disclose your personal data to courts, law enforcement agencies, or government authorities if required to:
  - Comply with legal obligations or processes.
  - Respond to claims against `tianji.msgbye.com`.
  - Address verified requests related to criminal investigations or alleged illegal activities.
  - Enforce our Terms of Use and other agreements.
  - Protect the rights, property, or personal safety of `tianji.msgbye.com`, its employees, and the public.

## Information Security

`tianji.msgbye.com` implements technical, administrative, and physical safeguards designed to prevent unauthorized access, use, or disclosure of the personal data we collect or store. These measures are intended to ensure a level of security appropriate to the risk involved. We regularly monitor our systems for vulnerabilities and potential attacks. However, no internet transmission is completely secure, so you provide personal data at your own discretion and risk.

## Contact Information

For questions or complaints about this Privacy Policy or our practices, please contact us by email at `moonrailgun@gmail.com`.
