Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
50e0cbf
feat: add VerticalCollapsibleSteps component for guided user onboarding
JustNZ Aug 5, 2025
919e259
feat: Implement project encryption management
JustNZ Aug 18, 2025
0c21ca5
refactor: Remove encryption settings from flow modals and related fun…
JustNZ Aug 18, 2025
2915383
chore(deps): bump eslint-config-next in /services/frontend
dependabot[bot] Aug 19, 2025
98c9263
docs: Update README and docker-compose for encryption configuration d…
JustNZ Aug 19, 2025
8414bee
chore(deps): bump @radix-ui/react-popover in /services/frontend
dependabot[bot] Aug 19, 2025
559766e
Merge pull request #267 from v1Flows/feature/new_encryption
JustNZ Aug 19, 2025
2970b1a
Merge pull request #268 from v1Flows/dependabot/npm_and_yarn/services…
JustNZ Aug 19, 2025
08d3712
Merge pull request #266 from v1Flows/dependabot/npm_and_yarn/services…
JustNZ Aug 19, 2025
55443f8
chore(deps): bump eslint-plugin-unused-imports in /services/frontend
dependabot[bot] Aug 19, 2025
8dc1dbc
chore(deps): bump the backend group in /services/backend with 2 updates
dependabot[bot] Aug 19, 2025
300f7e6
chore(deps): bump globals from 15.15.0 to 16.3.0 in /services/frontend
dependabot[bot] Aug 19, 2025
bfd260c
chore(deps): bump @react-aria/utils in /services/frontend
dependabot[bot] Aug 19, 2025
b25f123
feat: integrate SWR for data fetching and cache management across com…
JustNZ Aug 7, 2025
45948b9
Merge pull request #271 from v1Flows/chore/new-welcome
JustNZ Aug 19, 2025
6f8c144
Refactor execution and flow components to utilize SWR for data fetchi…
JustNZ Aug 19, 2025
4171bcd
Merge pull request #269 from v1Flows/dependabot/npm_and_yarn/services…
JustNZ Aug 19, 2025
6a70da6
Merge pull request #270 from v1Flows/dependabot/go_modules/services/b…
JustNZ Aug 19, 2025
31364a8
Merge pull request #272 from v1Flows/dependabot/npm_and_yarn/services…
JustNZ Aug 19, 2025
07ce3a8
Merge pull request #273 from v1Flows/dependabot/npm_and_yarn/services…
JustNZ Aug 19, 2025
cb0995f
refactor: replace useRouter with useRefreshCache for improved cache m…
JustNZ Aug 19, 2025
a18ee3c
Merge branch 'develop' into feature/swr
JustNZ Aug 19, 2025
dcd02a4
refactor: remove unused Reloader component from AdminRunnersHeading a…
JustNZ Aug 19, 2025
2b476fd
Merge pull request #275 from v1Flows/feature/swr
JustNZ Aug 19, 2025
4b8f673
feat: add Flow Failure Pipelines component with drag-and-drop functio…
JustNZ Aug 19, 2025
f515945
refactor: streamline FlowHeading and FlowSettings components for impr…
JustNZ Aug 19, 2025
12cd2fa
feat: Update FlowHeading component to remove secondary color from button
JustNZ Aug 21, 2025
78eb4cc
Merge pull request #276 from v1Flows/chore/ui-redesign
JustNZ Aug 21, 2025
60fc9ba
refactor: remove PostCSS configuration and update Tailwind setup
JustNZ Aug 21, 2025
997618f
fix: downgrade @internationalized/date to version 3.7.0 for compatibi…
JustNZ Aug 21, 2025
0bffd0d
Merge pull request #277 from v1Flows/feature/tailwind-v4
JustNZ Aug 21, 2025
86db1f8
feat: add migrations for alerts, flows, executions, and runners model…
JustNZ Sep 4, 2025
a3f63a6
feat: add delete alert modal and integrate with alert details drawer
JustNZ Sep 5, 2025
032e3ff
feat: implement alerts management with pagination, filtering, and cac…
JustNZ Sep 8, 2025
ebf4104
feat: enhance user stats endpoint to include detailed execution and a…
JustNZ Sep 8, 2025
285763d
feat: refactor Alerts component to support multiple flows and improve…
JustNZ Sep 8, 2025
e1be5d7
chore(deps): bump the backend group in /services/backend with 2 updates
dependabot[bot] Sep 8, 2025
f545985
chore(deps): bump next from 15.3.3 to 15.5.2 in /services/frontend
dependabot[bot] Sep 8, 2025
37e4705
feat: enhance flow update functionality to include patterns and alert…
JustNZ Sep 9, 2025
dffcde8
fix: update category assignment in BarChartCard to handle missing cat…
JustNZ Sep 9, 2025
3b71f0a
Merge pull request #285 from v1Flows/feature/alertflow-migration
JustNZ Sep 9, 2025
34611f8
feat: Implement setup mode for application initialization
JustNZ Sep 9, 2025
3b92565
Merge pull request #284 from v1Flows/dependabot/npm_and_yarn/services…
JustNZ Sep 9, 2025
c47ec73
Merge pull request #282 from v1Flows/dependabot/go_modules/services/b…
JustNZ Sep 9, 2025
68233ad
fix: streamline state initialization in AdminSettings and update Logi…
JustNZ Sep 9, 2025
045ec09
fix: update Node.js version in Dockerfile and correct backend config …
JustNZ Sep 9, 2025
0cc99b0
feat: add manual build workflow for Docker images
JustNZ Sep 9, 2025
d78bac6
Merge pull request #287 from v1Flows/feature/action
JustNZ Sep 9, 2025
ff16376
fix: ensure correct branch reference during code checkout in manual b…
JustNZ Sep 9, 2025
f9b59da
Merge pull request #288 from v1Flows/feature/action
JustNZ Sep 9, 2025
fa0a1b3
fix: update backend port to 8080 in setup mode and related components
JustNZ Sep 9, 2025
e2471e3
fix: ensure correct ownership for /etc/exflow directory in Dockerfile
JustNZ Sep 10, 2025
a00e6ef
feat: implement config file persistence in startup script
JustNZ Sep 10, 2025
45773fc
fix: update config file paths in startup script for backend compatibi…
JustNZ Sep 10, 2025
312ba67
fix: ensure backend build is copied to the final image
JustNZ Sep 10, 2025
469fac4
fix: update paths for backend and frontend config files in Dockerfile…
JustNZ Sep 10, 2025
ee6989c
fix: update Node.js version in Dockerfile and streamline backend bina…
JustNZ Sep 10, 2025
4c40fcb
fix: update CheckSetupStatus to use dynamic config file paths and enh…
JustNZ Sep 10, 2025
85c83b1
fix: update config file paths in Dockerfile and backend to ensure con…
JustNZ Sep 10, 2025
2c2a30f
fix: update Dockerfile and docker-compose to use consistent config fi…
JustNZ Sep 10, 2025
d34d1fc
fix: update user and group ownership in Dockerfile for consistency
JustNZ Sep 10, 2025
7c2e339
Merge pull request #289 from v1Flows/feature/setup
JustNZ Sep 10, 2025
f6762f3
chore(deps): bump @react-aria/i18n in /services/frontend
dependabot[bot] Sep 10, 2025
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
43 changes: 43 additions & 0 deletions .github/workflows/build-image-manual.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Build Image Manual Branch

on:
workflow_dispatch:
inputs:
branch:
description: 'Branch to build and release'
required: true
default: 'develop'

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v2
with:
ref: ${{ github.event.inputs.branch }}

- name: Build Frontend Docker Image
run: docker build . --file services/frontend/Dockerfile --tag justnz/exflow:frontend-${{ github.sha }} --tag justnz/exflow:frontend-${{ github.ref_name }}

- name: Build Backend Docker Image
run: docker build . --file services/backend/Dockerfile --tag justnz/exflow:backend-${{ github.sha }} --tag justnz/exflow:backend-${{ github.ref_name }}

- name: Build exFlow Docker Image
run: docker build . --file Dockerfile --tag justnz/exflow:${{ github.sha }} --tag justnz/exflow:${{ github.ref_name }}

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Push Docker Images
run: |
docker push justnz/exflow:frontend-${{ github.sha }}
docker push justnz/exflow:frontend-${{ github.ref_name }}
docker push justnz/exflow:backend-${{ github.sha }}
docker push justnz/exflow:backend-${{ github.ref_name }}
docker push justnz/exflow:${{ github.sha }}
docker push justnz/exflow:${{ github.ref_name }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ services/backend/*.exe~
services/backend/*.dll
services/backend/*.so
services/backend/*.dylib
services/backend/*.sql

# Test binary, built with `go test -c`
services/backend/*.test
Expand Down
13 changes: 6 additions & 7 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FROM node:23-alpine AS base
FROM node:24.7-alpine AS base

# Stage 1: Build the frontend
FROM node:23-alpine AS frontend-builder
FROM node:24.7-alpine AS frontend-builder
RUN apk add --no-cache libc6-compat
WORKDIR /app/frontend
COPY services/frontend/package.json services/frontend/pnpm-lock.yaml ./
Expand Down Expand Up @@ -49,11 +49,10 @@ RUN mkdir .next \
COPY --from=frontend-builder --chown=nextjs:nodejs /app/frontend/.next/standalone ./
COPY --from=frontend-builder --chown=nextjs:nodejs /app/frontend/.next/static ./.next/static

# Copy .env file to the working directory
COPY --from=frontend-builder --chown=nextjs:nodejs /app/frontend/.env /app/.env
RUN chown -R nextjs:nodejs /app

RUN mkdir -p /etc/exflow
COPY services/backend/config/config.yaml /etc/exflow/backend_config.yaml
RUN mkdir -p /etc/exflow \
&& chown -R nextjs:nodejs /etc/exflow

# Set environment variables
ENV NODE_ENV=production
Expand All @@ -69,4 +68,4 @@ USER nextjs
ENTRYPOINT ["/sbin/tini", "--"]

# Start the backend and frontend
CMD ["sh", "-c", "./exflow-backend --config /etc/exflow/backend_config.yaml & node /app/server.js"]
CMD ["sh", "-c", "./exflow-backend --config /etc/exflow/config.yaml & node /app/server.js"]
110 changes: 110 additions & 0 deletions ENCRYPTION_SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Secure Project-Based Encryption Setup

## Overview

The enhanced encryption system now uses **key derivation** instead of storing encryption keys directly in the database. This significantly improves security by ensuring that even if someone gains access to your database, they cannot decrypt the data without the master secret.

## How It Works

1. **Master Secret**: A single secret stored outside the database (environment variable, config file, external key management system)
2. **Project Salts**: Random salts generated per project and stored in the database
3. **Key Derivation**: Encryption keys are derived using PBKDF2(master_secret + project_salt)

Even if an attacker gains access to your database, they only see:
- Encrypted data
- Random salts (which are useless without the master secret)

## Configuration

### Option 1: Environment Variable (Recommended for Production)
```bash
# Set the master secret as an environment variable
export EXFLOW_ENCRYPTION_MASTER_SECRET="your-very-long-and-secure-master-secret-here"
```

### Option 2: Configuration File
```yaml
# In your backend config.yaml
encryption:
master_secret: "your-very-long-and-secure-master-secret-here"
# Fallback key for legacy data (optional)
key: "legacy-key-for-backward-compatibility"
```

## Master Secret Requirements

- **Length**: Minimum 32 characters, recommended 64+ characters
- **Randomness**: Use a cryptographically secure random generator
- **Characters**: Include letters, numbers, and symbols
- **Uniqueness**: Must be unique per exFlow installation

### Generate a Secure Master Secret

```bash
# Option 1: Using OpenSSL
openssl rand -base64 64

# Option 2: Using Python
python3 -c "import secrets; print(secrets.token_urlsafe(64))"

# Option 3: Using Go
go run -c "package main; import (\"crypto/rand\", \"encoding/base64\", \"fmt\"); func main() { b := make([]byte, 64); rand.Read(b); fmt.Println(base64.URLEncoding.EncodeToString(b)) }"
```

## Security Benefits

1. **Database Compromise Protection**: Even with full database access, encrypted data remains secure
2. **Per-Project Isolation**: Each project uses a unique derived key
3. **Key Rotation**: Changing the master secret or project salt rotates all encryption
4. **Audit Trail**: Key derivation can be logged and monitored
5. **Compliance**: Meets most regulatory requirements for encryption key management

## Migration from Legacy System

The system maintains backward compatibility:

1. **New Projects**: Automatically use the secure key derivation system
2. **Existing Projects**: Continue working with existing keys until migrated
3. **Gradual Migration**: Projects can be migrated one by one using the key rotation feature

## Best Practices

### Storage
- **Never** store the master secret in the database
- Use environment variables or external key management systems
- Rotate the master secret periodically (quarterly/annually)
- Keep secure backups of the master secret

### Access Control
- Limit access to the master secret to essential personnel only
- Use separate master secrets for different environments (dev/staging/prod)
- Log all access to encryption keys

### Monitoring
- Monitor for unusual encryption/decryption patterns
- Alert on encryption failures
- Regular security audits of key management processes

## Troubleshooting

### "Master secret not configured" Error
1. Ensure the master secret is set in your configuration
2. Restart the backend service after setting the secret
3. Check environment variable spelling

### "Failed to decrypt" Errors
1. Verify the master secret hasn't changed
2. Check if the project salt was corrupted
3. Consider falling back to legacy key mode temporarily

### Performance Considerations
- Key derivation adds ~1-2ms per operation
- Consider caching derived keys in memory for high-throughput scenarios
- Monitor CPU usage during bulk encryption operations

## Example Implementation

```go
// Environment variable
masterSecret := os.Getenv("EXFLOW_ENCRYPTION_MASTER_SECRET")
```
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,10 @@ To get started with the exFlow project, follow these steps:
password: postgres

encryption:
enabled: true
# maximum 32 characters
key: null
# Minimum 32 characters, recommended 64+ characters
master_secret: "your-very-long-and-secure-master-secret-here"
# Fallback key for legacy data (optional)
key: "legacy-key-for-backward-compatibility"

jwt:
secret: null
Expand Down
19 changes: 4 additions & 15 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,9 @@ services:
- "3000:3000" # exFlow frontend
- "8080:8080" # exFlow backend
volumes:
- ./config.yaml:/etc/exflow/backend_config.yaml
# environment:
# Adjust these if your config.yaml uses different DB settings
# BACKEND_LOG_LEVEL: info
# BACKEND_PORT: 8080
# BACKEND_DATABASE_SERVER: db
# BACKEND_DATABASE_PORT: 5432
# BACKEND_DATABASE_NAME: postgres
# BACKEND_DATABASE_USER: postgres
# BACKEND_DATABASE_PASSWORD: postgres
# BACKEND_ENCRYPTION_ENABLED: "true"
# BACKEND_ENCRYPTION_KEY: "change-me"
# BACKEND_JWT_SECRET: "change-me"
entrypoint: ["/bin/sh", "-c", "until pg_isready -h db -p 5432 -U postgres; do sleep 1; done; exec ./exflow-backend --config /etc/exflow/backend_config.yaml & exec node /app/server.js"]
- exflow_data:/etc/exflow
entrypoint: ["/bin/sh", "-c", "until pg_isready -h db -p 5432 -U postgres; do sleep 1; done; exec ./exflow-backend --config /etc/exflow/config.yaml & exec node /app/server.js"]

volumes:
db_data:
db_data:
exflow_data:
19 changes: 12 additions & 7 deletions services/backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
FROM golang:1.24-alpine as builder

WORKDIR /backend
WORKDIR /app/backend

COPY services/backend/go.mod services/backend/go.sum ./
RUN go mod download

COPY services/backend/ ./

# Build
RUN CGO_ENABLED=0 GOOS=linux go build -o /exflow-backend
RUN go build -o exflow-backend

FROM alpine:3.12 as runner
FROM alpine:3.22 as runner
WORKDIR /app

COPY --from=builder /exflow-backend /exflow-backend
COPY --from=builder /app/backend/exflow-backend /app/

RUN mkdir /app/config
COPY services/backend/config/config.yaml /etc/exflow/backend_config.yaml
RUN addgroup --system --gid 1001 exflow
RUN adduser --system --uid 1001 exflow

RUN mkdir -p /etc/exflow \
&& chown -R exflow:exflow /etc/exflow

RUN chown -R exflow:exflow /app

VOLUME [ "/etc/exflow" ]

EXPOSE 8080

CMD [ "/exflow-backend", "--config", "/etc/exflow/backend_config.yaml" ]
CMD [ "/exflow-backend", "--config", "/etc/exflow/config.yaml" ]
7 changes: 4 additions & 3 deletions services/backend/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ database:
password: postgres

encryption:
enabled: true
# maximum 32 characters
key: null
# Minimum 32 characters, recommended 64+ characters
master_secret: "your-very-long-and-secure-master-secret-here"
# Fallback key for legacy data (optional)
key: "legacy-key-for-backward-compatibility"

jwt:
secret: null
Expand Down
10 changes: 7 additions & 3 deletions services/backend/config/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ type JWTConf struct {
}

type EncryptionConf struct {
Enabled bool `mapstructure:"enabled" validate:"required"`
Key string `mapstructure:"key"`
Key string `mapstructure:"key"`
MasterSecret string `mapstructure:"master_secret" validate:"required"`
}

type RunnerConf struct {
Expand Down Expand Up @@ -84,8 +84,8 @@ func (cm *ConfigurationManager) LoadConfig(configFile string) error {
"database.name": "BACKEND_DATABASE_NAME",
"database.user": "BACKEND_DATABASE_USER",
"database.password": "BACKEND_DATABASE_PASSWORD",
"encryption.enabled": "BACKEND_ENCRYPTION_ENABLED",
"encryption.key": "BACKEND_ENCRYPTION_KEY",
"encryption.master_secret": "BACKEND_ENCRYPTION_MASTER_SECRET",
"jwt.secret": "BACKEND_JWT_SECRET",
"runner.shared_runner_secret": "BACKEND_RUNNER_SHARED_RUNNER_SECRET",
}
Expand Down Expand Up @@ -118,6 +118,10 @@ func (cm *ConfigurationManager) LoadConfig(configFile string) error {
// Assign to package-level variable for global access
Config = &config

if config.Encryption.MasterSecret == "" {
log.Fatal("Master secret is required for encryption")
}

log.WithFields(log.Fields{
"file": configFile,
"content": cm.viper.AllSettings(),
Expand Down
60 changes: 60 additions & 0 deletions services/backend/database/migrations/10_flows_type_col.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package migrations

import (
"context"
"fmt"

log "github.com/sirupsen/logrus"
"github.com/uptrace/bun"
)

func init() {
Migrations.MustRegister(func(ctx context.Context, db *bun.DB) error {
return addTypeToFlows(ctx, db)
}, func(ctx context.Context, db *bun.DB) error {
return removeTypeFromFlows(ctx, db)
})
}

func addTypeToFlows(ctx context.Context, db *bun.DB) error {
// add type column
exists, err := columnExists(ctx, db, "flows", "type")
if err != nil {
return fmt.Errorf("failed to check if type column exists: %v", err)
}
if !exists {
_, err := db.NewAddColumn().
Table("flows").
ColumnExpr("type TEXT DEFAULT 'default'").
Exec(ctx)

if err != nil {
return fmt.Errorf("failed to add type column to flows table: %v", err)
}
} else {
log.Debug("type column already exists in flows table")
}

return nil
}

func removeTypeFromFlows(ctx context.Context, db *bun.DB) error {
exists, err := columnExists(ctx, db, "flows", "type")
if err != nil {
return fmt.Errorf("failed to check if type column exists: %v", err)
}
if exists {
_, err := db.NewDropColumn().
Table("flows").
Column("type").
Exec(ctx)

if err != nil {
return fmt.Errorf("failed to remove type column from flows table: %v", err)
}
} else {
log.Debug("type column already removed from flows table")
}

return nil
}
Loading
Loading