Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
0d5671c
fix: rename SCIM user audit entity type from User to ScimUser
TerrifiedBug Mar 9, 2026
cf1ff26
feat: add combined SCIM filter to audit log entity type dropdown
TerrifiedBug Mar 9, 2026
b9cef32
feat: add UserPreference model and API for default team/env
TerrifiedBug Mar 9, 2026
130c6b5
feat: add deployedBy/deployedAt fields to DeployRequest
TerrifiedBug Mar 9, 2026
9c1c3ec
feat: add star/default selector for teams and environments
TerrifiedBug Mar 9, 2026
ba76200
feat: add admin default environment setting for teams
TerrifiedBug Mar 9, 2026
f7cf0da
feat: decouple deploy approval from execution
TerrifiedBug Mar 9, 2026
6867d98
feat: add event-based alert metrics and nullable threshold fields
TerrifiedBug Mar 9, 2026
083ee15
feat: add fireEventAlert helper for event-based alerting
TerrifiedBug Mar 9, 2026
09abb66
feat: deploy dialog shows approved requests with deploy/cancel buttons
TerrifiedBug Mar 9, 2026
44928e5
feat: split settings page into sub-routes
TerrifiedBug Mar 9, 2026
4904054
feat: settings sidebar navigation with animated mode swap
TerrifiedBug Mar 9, 2026
94c8b93
feat: event-based alert rule creation UI with conditional threshold f…
TerrifiedBug Mar 9, 2026
97de4ba
feat: wire event-based alerts to deploy, SCIM, backup, fleet, and cer…
TerrifiedBug Mar 9, 2026
bbac625
docs: update docs for event alerts, deploy approvals, and settings na…
TerrifiedBug Mar 9, 2026
553591e
fix: validate deploy request statuses and preference key length
TerrifiedBug Mar 9, 2026
af44c22
fix: revert deploy request status on non-throwing deploy failure
TerrifiedBug Mar 9, 2026
f7200b0
fix: restore cancel ownership guard for pending requests and isolate …
TerrifiedBug Mar 9, 2026
1eb09ca
fix: skip sync-failed alert on SCIM 409 conflict responses
TerrifiedBug Mar 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion docs/public/operations/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,24 @@ VF_LOG_LEVEL=info

## System settings (UI)

The following settings are configured through the **Settings** page in the VectorFlow UI. Only Super Admins can access this page. These values are stored in the database and take effect immediately.
The following settings are configured through the **Settings** page in the VectorFlow UI. These values are stored in the database and take effect immediately.

### Settings navigation

The Settings page has its own dedicated sidebar navigation, separate from the main application sidebar. When you click **Settings** in the main navigation, the sidebar transitions to show the settings menu organized into four sections:

| Section | Pages | Visibility |
|---------|-------|------------|
| **System** | Fleet, Backup | Super Admin only |
| **Security** | Authentication, SCIM | Super Admin only |
| **Organization** | Team, Users, Service Accounts | Team: Admin+, Users: Super Admin, Service Accounts: Admin+ |
| **Operations** | Audit | Admin+ |

Click the back arrow at the top of the settings sidebar to return to the main navigation. The transition between the main sidebar and settings sidebar is animated for a smooth experience.

{% hint style="info" %}
Team admins see a subset of the settings pages (Team, Service Accounts, Audit). Super admins see all settings pages. Viewers and editors do not have access to the Settings page.
{% endhint %}

### Fleet settings

Expand Down
12 changes: 11 additions & 1 deletion docs/public/operations/scim.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,17 @@ GET /api/scim/v2/Groups?filter=displayName eq "Platform Team"
- The token is shown only once when generated; VectorFlow does not store the plaintext
- SCIM endpoints require a valid bearer token on every request
- Disabling SCIM clears the stored token
- All SCIM operations are recorded in the audit log
- All SCIM operations are recorded in the audit log under the **ScimUser** entity type

### Audit logging

SCIM user operations (create, update, deactivate, delete) are logged with the `ScimUser` entity type to distinguish them from manual user operations. On the **Audit** page, you can filter by:

- **ScimUser** -- shows only SCIM user provisioning events
- **ScimGroup** -- shows only SCIM group operations
- **SCIM (All)** -- a combined filter that shows all SCIM-related activity (both user and group operations) in a single view

This makes it easy to audit all identity provider-driven changes for compliance purposes.

{% hint style="info" %}
SCIM provisioning works best alongside OIDC/SSO. Users created via SCIM receive a random password and should authenticate through your identity provider, not with local credentials.
Expand Down
8 changes: 7 additions & 1 deletion docs/public/operations/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,13 @@ Every mutation in VectorFlow is logged to an audit trail. Audit entries include:

Sensitive fields (passwords, tokens, secrets) are automatically redacted in audit log entries.

View the audit log from the **Audit** page in the sidebar.
View the audit log from the **Audit** page in the sidebar. The audit log supports filtering by entity type, including dedicated filters for SCIM operations:

- **ScimUser** -- SCIM user provisioning events (create, update, deactivate)
- **ScimGroup** -- SCIM group operations (create, update members, delete)
- **SCIM (All)** -- Combined filter showing all SCIM activity in one view

These filters make it straightforward to review all identity provider-driven changes for compliance audits.

## Security hardening checklist

Expand Down
25 changes: 25 additions & 0 deletions docs/public/user-guide/alerts.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ Click **Create Rule**. The rule is enabled by default and begins evaluating on t

### Supported metrics

VectorFlow supports three categories of alert metrics: **Infrastructure** metrics that monitor resource utilization with thresholds, **Binary** metrics that fire on detected conditions, and **Event** metrics that fire when specific system events occur.

#### Infrastructure metrics

| Metric | Type | Description |
|--------|------|-------------|
| **CPU Usage** | Percentage | CPU utilization derived from cumulative CPU seconds. |
Expand All @@ -57,6 +61,27 @@ Click **Create Rule**. The rule is enabled by default and begins evaluating on t

Percentage-based metrics use the conditions **>** (greater than), **<** (less than), or **=** (equals) against a threshold value. Binary metrics (Node Unreachable, Pipeline Crashed) fire automatically when the condition is detected -- no threshold is needed.

#### Event metrics

Event metrics fire whenever a specific system event occurs. Unlike infrastructure metrics, they have **no threshold** -- the alert triggers on each occurrence. Event rules are created the same way as infrastructure rules, but you select a metric from the **Events** category in the metric dropdown.

| Metric | Description |
|--------|-------------|
| **Deploy Requested** | A deploy request was submitted for approval. |
| **Deploy Completed** | A pipeline was successfully deployed to agents. |
| **Deploy Rejected** | A deploy request was rejected by a reviewer. |
| **Deploy Cancelled** | A deploy request was cancelled. |
| **New Version Available** | A new VectorFlow server version is available. |
| **SCIM Sync Failed** | A SCIM provisioning operation failed. |
| **Backup Failed** | A scheduled database backup failed. |
| **Certificate Expiring** | A TLS certificate is approaching its expiration date. |
| **Node Joined** | A new agent node enrolled in the environment. |
| **Node Left** | An agent node was removed or disconnected from the environment. |

{% hint style="info" %}
Event alerts use the same notification channels as infrastructure alerts (Slack, Email, PagerDuty, Webhook). You can route event alerts to specific channels by linking channels to the rule, just like any other alert rule.
{% endhint %}

### Condition evaluation

Alert rules are evaluated during each agent heartbeat cycle. The evaluation logic works as follows:
Expand Down
40 changes: 36 additions & 4 deletions docs/public/user-guide/environments.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,24 @@ The environment selector is the dropdown in the header bar. Switching it changes
When you switch environments, the pipeline list, fleet view, and alerts page update to show only resources for that environment. Your selection is persisted across sessions.
{% endhint %}

### Default environment

You can set a **default environment** so VectorFlow automatically selects it when you log in or switch teams.

**User default (per-user):** Click the star icon next to any environment in the environment selector dropdown. The starred environment becomes your personal default for that team. Click the star again to clear it.

**Admin default (per-team):** Team admins can set a team-wide default environment from **Settings > Team**. This applies to all team members who have not set their own personal default.

The fallback chain when loading the app is:

1. **User default** -- your personally starred environment (if set)
2. **Admin team default** -- the team-level default environment (if configured by an admin)
3. **First in list** -- the first environment alphabetically

{% hint style="info" %}
The team selector in the header also supports starring. Click the star next to a team to set it as your default team on login.
{% endhint %}

## Creating an environment

{% stepper %}
Expand Down Expand Up @@ -91,7 +109,7 @@ Secrets and certificates are stripped during promotion. After promoting a pipeli

## Deploy approval

Environments can require **admin approval** before pipelines are deployed. This is useful for production environments where you want a second pair of eyes on every configuration change.
Environments can require **approval** before pipelines are deployed. This is useful for production environments where you want a second pair of eyes on every configuration change. Approval and deployment are **separate actions** -- a reviewer approves the request, and then anyone with deploy access can execute the deployment.

### Enabling approval

Expand All @@ -112,13 +130,27 @@ Click **Save** to apply the change.

When enabled:
- Users with the **Editor** role will see a **Request Deploy** button instead of **Publish to Agents** in the deploy dialog. Their deploy requests are queued for review.
- Users with the **Admin** role can deploy directly (no approval needed) and can review, approve, or reject pending requests from other users.
- A **Pending Approval** badge appears on the pipeline list and in the pipeline editor toolbar while a request is outstanding.
- Any team member with deploy access (editor or admin) can **approve** a pending request. Approval does not automatically deploy -- it marks the request as ready.
- Once approved, any team member with deploy access can **deploy** the approved request. The deploy dialog shows a **Deploy** button for approved requests.
- Approved requests can also be **cancelled** by anyone with deploy access if the deployment is no longer needed.
- Users with the **Admin** role can deploy directly (no approval needed), bypassing the approval flow entirely.
- A **Pending Approval** or **Approved** badge appears on the pipeline list and in the pipeline editor toolbar to indicate the request status.

{% hint style="info" %}
An admin cannot approve their own deploy request. This ensures a genuine four-eyes review process.
The person who submitted a deploy request cannot approve their own request. This enforces a four-eyes principle -- a second team member must always review the deployment.
{% endhint %}

### Deploy tracking

VectorFlow tracks who actually deployed a pipeline separately from who approved it. The deploy history records:

- **Requested by** -- the user who submitted the deploy request
- **Approved by** -- the user who approved the request
- **Deployed by** -- the user who executed the deployment
- **Status** -- the request lifecycle: `PENDING` → `APPROVED` → `DEPLOYED` (or `REJECTED` / `CANCELLED`)

This separation provides a clear audit trail for compliance, especially in regulated environments where you need to know exactly who authorized and executed each deployment.

For more details on how the approval workflow operates, see [Pipelines -- Deploy approval workflows](pipelines.md#deploy-approval-workflows).

## Editing and deleting environments
Expand Down
25 changes: 19 additions & 6 deletions docs/public/user-guide/pipelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,18 +182,31 @@ Tags are metadata labels only -- they do not enforce any access controls or data

## Deploy approval workflows

Environments can optionally require **deploy approval** before a pipeline goes live. When enabled, editors who click **Deploy** will submit a deploy request instead of deploying directly. Another team member (editor or admin) can then review, approve, or reject the request.
Environments can optionally require **deploy approval** before a pipeline goes live. When enabled, editors who click **Deploy** will submit a deploy request instead of deploying directly. Approval and deployment are **separate actions** -- a reviewer approves the request, and then anyone with deploy access can execute the deployment.

### How it works

1. An admin enables **Require approval for deploys** on the environment settings page (see [Environments](environments.md#deploy-approval)).
2. When an editor clicks **Deploy** in the pipeline editor, the deploy dialog shows a **Request Deploy** button instead of **Publish to Agents**.
3. The editor submits a deploy request with a changelog entry. The pipeline list and pipeline editor toolbar show a **Pending Approval** badge.
4. Another team member (editor or admin) opens the deploy dialog for the pipeline and sees the request in **review mode** — displaying the requester, changelog, and a config diff.
5. The reviewer can **Approve & Deploy** (which immediately deploys the pipeline) or **Reject** (with an optional note).
4. Another team member (editor or admin) opens the deploy dialog for the pipeline and sees the request in **review mode** -- displaying the requester, changelog, and a config diff.
5. The reviewer clicks **Approve** to mark the request as approved. This does **not** deploy the pipeline.
6. Once approved, any team member with deploy access can click **Deploy** on the approved request to push the configuration to agents.

### Request lifecycle

A deploy request moves through these statuses:

| Status | Description |
|--------|-------------|
| **Pending** | The request is waiting for review. |
| **Approved** | A reviewer has approved the request. It is ready to be deployed. |
| **Deployed** | The approved request has been deployed to agents. |
| **Rejected** | A reviewer has rejected the request (with an optional note). |
| **Cancelled** | The request was cancelled before deployment. |

{% hint style="warning" %}
**Self-approval is blocked.** The person who submitted a deploy request cannot approve their own request. This enforces a four-eyes principle a second team member must always review and approve the deployment.
**Self-approval is blocked.** The person who submitted a deploy request cannot approve their own request. This enforces a four-eyes principle -- a second team member must always review the deployment.
{% endhint %}

{% hint style="info" %}
Expand All @@ -202,11 +215,11 @@ Admins can always deploy directly, even when approval is required. When an admin

### Cancelling a request

The editor who submitted a pending deploy request can cancel it from the pipeline editor toolbar by clicking the **X** button next to the **Pending Approval** badge.
Anyone with deploy access can cancel a pending or approved deploy request. For pending requests, click the **X** button next to the **Pending Approval** badge in the pipeline editor toolbar. For approved requests, click **Cancel** in the deploy dialog.

### Pipeline list indicators

Pipelines with pending deploy requests show a **Pending Approval** badge in the status column on the Pipelines page, so admins can quickly identify which pipelines need attention.
Pipelines with pending or approved deploy requests show a status badge (**Pending Approval** or **Approved**) in the status column on the Pipelines page, so team members can quickly identify which pipelines need attention.

## Filtering by environment

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
-- CreateTable
CREATE TABLE "UserPreference" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"key" TEXT NOT NULL,
"value" TEXT NOT NULL,

CONSTRAINT "UserPreference_pkey" PRIMARY KEY ("id")
);

-- AlterTable
ALTER TABLE "Team" ADD COLUMN "defaultEnvironmentId" TEXT;

-- CreateIndex
CREATE INDEX "UserPreference_userId_idx" ON "UserPreference"("userId");

-- CreateIndex
CREATE UNIQUE INDEX "UserPreference_userId_key_key" ON "UserPreference"("userId", "key");

-- AddForeignKey
ALTER TABLE "UserPreference" ADD CONSTRAINT "UserPreference_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "Team" ADD CONSTRAINT "Team_defaultEnvironmentId_fkey" FOREIGN KEY ("defaultEnvironmentId") REFERENCES "Environment"("id") ON DELETE SET NULL ON UPDATE CASCADE;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-- AlterTable
ALTER TABLE "DeployRequest" ADD COLUMN "deployedAt" TIMESTAMP(3);
ALTER TABLE "DeployRequest" ADD COLUMN "deployedById" TEXT;

-- AddForeignKey
ALTER TABLE "DeployRequest" ADD CONSTRAINT "DeployRequest_deployedById_fkey" FOREIGN KEY ("deployedById") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
-- Add new event-based values to AlertMetric enum
ALTER TYPE "AlertMetric" ADD VALUE 'deploy_requested';
ALTER TYPE "AlertMetric" ADD VALUE 'deploy_completed';
ALTER TYPE "AlertMetric" ADD VALUE 'deploy_rejected';
ALTER TYPE "AlertMetric" ADD VALUE 'deploy_cancelled';
ALTER TYPE "AlertMetric" ADD VALUE 'new_version_available';
ALTER TYPE "AlertMetric" ADD VALUE 'scim_sync_failed';
ALTER TYPE "AlertMetric" ADD VALUE 'backup_failed';
ALTER TYPE "AlertMetric" ADD VALUE 'certificate_expiring';
ALTER TYPE "AlertMetric" ADD VALUE 'node_joined';
ALTER TYPE "AlertMetric" ADD VALUE 'node_left';

-- Make threshold fields nullable for event-based rules
ALTER TABLE "AlertRule" ALTER COLUMN "condition" DROP NOT NULL;
ALTER TABLE "AlertRule" ALTER COLUMN "threshold" DROP NOT NULL;
ALTER TABLE "AlertRule" ALTER COLUMN "durationSeconds" DROP NOT NULL;
ALTER TABLE "AlertRule" ALTER COLUMN "durationSeconds" DROP DEFAULT;
60 changes: 46 additions & 14 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -33,25 +33,40 @@ model User {
serviceAccounts ServiceAccount[]
deployRequestsMade DeployRequest[] @relation("deployRequester")
deployRequestsReviewed DeployRequest[] @relation("deployReviewer")
deployRequestsExecuted DeployRequest[] @relation("deployExecutor")
preferences UserPreference[]
createdAt DateTime @default(now())
}

model UserPreference {
id String @id @default(cuid())
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
key String
value String

@@unique([userId, key])
@@index([userId])
}

enum AuthMethod {
LOCAL
OIDC
}

model Team {
id String @id @default(cuid())
name String
requireTwoFactor Boolean @default(false)
members TeamMember[]
environments Environment[]
templates Template[]
vrlSnippets VrlSnippet[]
alertRules AlertRule[]
availableTags Json? @default("[]") // string[] of admin-defined classification tags
createdAt DateTime @default(now())
id String @id @default(cuid())
name String
requireTwoFactor Boolean @default(false)
defaultEnvironmentId String?
defaultEnvironment Environment? @relation("teamDefault", fields: [defaultEnvironmentId], references: [id], onDelete: SetNull)
members TeamMember[]
environments Environment[]
templates Template[]
vrlSnippets VrlSnippet[]
alertRules AlertRule[]
availableTags Json? @default("[]") // string[] of admin-defined classification tags
createdAt DateTime @default(now())
}

model ScimGroup {
Expand Down Expand Up @@ -117,6 +132,7 @@ model Environment {
notificationChannels NotificationChannel[]
serviceAccounts ServiceAccount[]
deployRequests DeployRequest[]
teamDefaults Team[] @relation("teamDefault")
createdAt DateTime @default(now())
}

Expand Down Expand Up @@ -540,25 +556,41 @@ model DeployRequest {
configYaml String
changelog String
nodeSelector Json?
status String @default("PENDING") // PENDING | APPROVED | REJECTED | CANCELLED
status String @default("PENDING") // PENDING | APPROVED | REJECTED | CANCELLED | DEPLOYED
reviewedById String?
reviewedBy User? @relation("deployReviewer", fields: [reviewedById], references: [id])
reviewNote String?
createdAt DateTime @default(now())
reviewedAt DateTime?
deployedById String?
deployedBy User? @relation("deployExecutor", fields: [deployedById], references: [id], onDelete: SetNull)
deployedAt DateTime?

@@index([pipelineId, status])
@@index([environmentId, status])
}

enum AlertMetric {
// Infrastructure (threshold-based)
node_unreachable
cpu_usage
memory_usage
disk_usage
error_rate
discarded_rate
pipeline_crashed

// Events (fire on occurrence)
deploy_requested
deploy_completed
deploy_rejected
deploy_cancelled
new_version_available
scim_sync_failed
backup_failed
certificate_expiring
node_joined
node_left
}

enum AlertCondition {
Expand All @@ -583,9 +615,9 @@ model AlertRule {
teamId String
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
metric AlertMetric
condition AlertCondition
threshold Float
durationSeconds Int @default(60)
condition AlertCondition?
threshold Float?
durationSeconds Int?
events AlertEvent[]
channels AlertRuleChannel[]
createdAt DateTime @default(now())
Expand Down
Loading
Loading